KMP、扩展KMP、Manacher习题
对于KMP算法,可以看我之前总结的这篇博客
hdu 3613 Best Reward
给一个字符串,字符由a~z构成,每个字符有一个权值。在某一点将字符串切成2半,若切成的字符串是回文的,则值为字符值之和,否则为0,问最大价值是多少?
设原串为S,S的逆记作T。则以T为主串,S为模式串做EKMP,若extend1[m-i]+m-i==m,则在i点切割,S[0~i)是回文的。若以S为主串,T为模式串做EKMP,若extend2[i]+i==m,则S[i,m)是回文的。具体为什么,可以看这个推理
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define maxN 500100
int v[], sum[maxN], nxt[maxN], extend1[maxN], extend2[maxN], cas;
char a[maxN], b[maxN]; void PRE_EKMP(char *x, int m, int *nxt) {
nxt[] = m;
int j = ;
while (j + < m && x[j] == x[j + ]) ++j;
nxt[] = j;
int k = ;
FOR(i, , m - ) {
int p = nxt[k] + k - ;
int L = nxt[i - k];
if (i + L - < p) nxt[i] = L;
else {
j = max(, p - i + );
while (i + j < m && x[i + j] == x[j]) ++j;
nxt[i] = j;
k = i;
}
}
}
void EKMP(char *x, int m, char *y, int n, int *nxt, int *extend) {
PRE_EKMP(x, m, nxt);
int j = ;
while (j < n && j < m && x[j] == y[j]) ++j;
extend[] = j;
int k = ;
FOR(i, , n - ) {
int p = extend[k] + k - ;
int L = nxt[i - k];
if (i + L < p + ) extend[i] = L;
else {
j = max(, p - i + );
while (i + j < n && j < m && y[i + j] == x[j]) ++j;
extend[i] = j;
k = i;
}
}
} int main () {
// freopen("data.in", "r", stdin);
scanf("%d", &cas);
while (cas--) {
sum[] = ;
FOR(i, , - ) scanf("%d", &v[i]);
scanf("%s", a);
int m = (int)strlen(a);
sum[] = v[a[] - 'a'];
FOR(i, , m - ) sum[i] = sum[i - ] + v[a[i] - 'a'];
FOR(i, , m - ) b[i] = a[m - i - ];
b[m] = ;
// EKMP(a,b)意味着a为模式串,去匹配b
EKMP(a, m, b, m, nxt, extend1); // 前缀
EKMP(b, m, a, m, nxt, extend2); // 后缀
int ans = -1e8;
FOR(i, , m - ) {
int sc = , j = m - i;
if (extend1[j] + j == m) sc += sum[i - ];
if (extend2[i] + i == m) sc += sum[m - ] - sum[i - ];
ans = max(ans, sc);
}
printf("%d\n", ans);
}
return ;
}
基于两个串a和b,问a在b中重复了几次。要对KMP进行一些修改,其实只是在模式串匹配完之后,ans++,并且让模式串的j回到原来的位置重来而已。
#include <cstdio>
#include <cstring>
using namespace std;
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define maxN 1000005
int nxt[maxN], n, m, cas, ans;
char a[maxN], b[maxN];
void getNxt(char *b, int m, int *nxt) {
nxt[] = -;
int i = , j = -;
while (i < m) {
if (j == - || b[i] == b[j]) ++i, ++j, nxt[i] = j;
else j = nxt[j];
}
}
void kmp(char *a, int n, char *b, int m) {
getNxt(b, m, nxt);
int i = , j = ;
while (i < n) {
while (- != j && a[i] != b[j]) j = nxt[j];
++i, ++j;
if (j >= m) {
++ans;
j = nxt[j];
}
}
}
int main () {
// freopen("data.in", "r", stdin);
scanf("%d", &cas);
while (cas--) {
scanf("%s%s", b, a);
n = (int)strlen(a), m = (int)strlen(b);
ans = ;
kmp(a, n, b, m);
printf("%d\n", ans);
}
return ;
}
poj 2752 Seek the Name, Seek the Fame
给你一个字符串,问前缀和后缀相同的字符串长度可以为多少?
考的是对next数组的理解。假设串为s,长度为L,那么next[L],即是s的最长前后缀长度,是答案之一,这里设这里的最长前缀为A,最长后缀为B。更短的”前缀-后缀串“必然也是A的前缀和B的后缀的公共部分,又因为A=B,那么问题变成了A的最长公共前后缀问题,如此便可不断回溯回去。
#include <cstdio>
#include <cstring>
using namespace std;
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define maxN 400005 char a[maxN];
int m, ans[maxN], nxt[maxN];
void getNxt(char *b, int m, int *nxt) {
nxt[] = -;
int i = , j = -;
while (i < m) {
if (j == - || b[i] == b[j]) ++i, ++j, nxt[i] = j;
else j = nxt[j];
}
}
int main () {
// freopen("data.in", "r", stdin);
while (~scanf("%s", a)) {
m = (int)strlen(a);
getNxt(a, m, nxt);
int cnt = ;
int cur = m, j = nxt[cur];
while (j) ans[++cnt] = j, j = nxt[j];
FOR(i, , cnt) printf("%d ", ans[cnt - i + ]);
printf("%d\n", m);
}
return ;
}
问的是一个字符串,其最多能由多少个循环节构成。可以参考这篇说明
#include <cstdio>
#include <cstring>
using namespace std;
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define maxN 1000005
char s[maxN];
int nxt[maxN];
void getNxt(char *b, int m, int *nxt) {
nxt[] = -;
int i = , j = -;
while (i < m) {
if (j == - || b[i] == b[j]) nxt[++i] = ++j;
else j = nxt[j];
}
}
int main () {
// freopen("data.in", "r", stdin);
while (~scanf("%s", s) && strcmp(s, ".")) {
int m = (int)strlen(s);
getNxt(s, m, nxt);
if (m % (m - nxt[m]) == )
printf("%d\n", m / (m - nxt[m]));
else
puts("");
}
return ;
}
问的是最少加入几个字符能使得这个串是循环的。
分几种情况:1,整个串无法被循环, 即nxt[m]=0,此时直接再来一个串接后面才行。
2,本身已经是循环串,此时m%(m-nxt[m])==0,直接输出0即可。
3,前缀是循环串,这个时候找到那个nxt[i]==0 (意味着前面就一个串,不循环),或者是i%(i-nxt[i])==0,此时即[0,i)这个串是循环串,得出最小循环节长度,设为L,答案就是L-后缀长度。
#include <cstdio>
#include <cstring>
using namespace std;
#define maxN 100006
int nxt[maxN], cas, idx;
char a[maxN];
void getNxt(char *v, int m) {
nxt[] = -;
int i = , j = -;
while (i < m) {
if (j == - || v[i] == v[j]) nxt[++i] = ++j;
else j = nxt[j]; if (nxt[i] == || i % (i - nxt[i]) == )
idx = i;
}
} int main () {
// freopen("data.in", "r", stdin);
scanf("%d", &cas);
while (cas--) {
scanf("%s", a);
int m = (int)strlen(a);
getNxt(a, m); if (nxt[m] == ) {
printf("%d\n", m);
} else if (m % (m - nxt[m]) == ) {
puts("");
} else {
// 循环节长度
int L = idx - nxt[idx];
int tail = m - idx;
printf("%d\n", L - tail);
}
}
return ;
}
hdu 3336 Count the string
问的是所有的前缀,在字符串中一共出现了几次?
假设某个前缀A和后缀B一样,那么B相当于给A贡献了B.length()分数。于是乎问题变成了:问有多少和前缀串相同的后缀串。但是因为如果单反前后缀一样就加分,会重复计算,比如说:
aaauvwaaa,第7和第8个a组成的aa会贡献2分,当加入第9个a成为aaa时,如果你又认为贡献3分,就会重复计算了第7个第8个连成的"aa"的分数。于是:当nxt[i]+1!=nxt[i+1]时,表示到i的后缀和到i+1的后缀是不一样的,才进行加分。
#include <cstdio>
using namespace std;
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define maxN 200005
int nxt[maxN], cas, n;
char a[maxN];
void getNxt(char *v, int m) {
nxt[] = -;
int i = , j = -;
while (i < m) {
if (j == - || v[i] == v[j]) nxt[++i] = ++j;
else j = nxt[j];
}
}
int main () {
// freopen("data.in", "r", stdin);
scanf("%d", &cas);
while (cas--) {
scanf("%d %s", &n, a);
getNxt(a, n);
int ans = (n + nxt[n]) % ;
FOR(i, , n - ) {
if (nxt[i] && nxt[i] + != nxt[i + ])
ans = (ans + nxt[i]) % ;
}
printf("%d\n", ans);
}
return ;
}
HDU 3374 String Problem KMP+最大最小表示法:
问的是一个字符串,其最小和最大表示的起始下标,以及各自出现的次数。
至于次数,不论是最大还是最小,当然都是一样的,求一个循环次数即可,用KMP的next数组。
而最小表示法的思想是怎么样的呢?:
令i=0,j=1,k=0,表示从i开始k长度和j开始k长度的字符串相同。
那么若s[i]=s[j],此时k++,若s[i]>s[j],意味着i位置的字典序比j位置的字典序更大,需要移动,又因为i开始和j开始有k个字符相同,所以j可以待定保留,i需要变化,变化到未知的地方,即i+=k+1即可。
相反,若s[i]<s[j],同理j+=k+1。最后i和j中的较小者,是第一次出现最小表示的下标。
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define maxN 1000005
int nxt[maxN];
char a[maxN];
void getNxt(char *v, int m) {
nxt[] = -;
int i = , j = -;
while (i < m) {
if (j < || v[i] == v[j]) nxt[++i] = ++j;
else j = nxt[j];
}
}
// mode=0:min else:max
int minMaxRep(char *s, int L, int mode) {
int i = , j = , k = , t;
while (i < L && j < L && k < L) {
t = s[(i + k) % L] - s[(j + k) % L];
if (t == ) ++k;
else {
if (mode == ) {
if (t > ) i += k + ;
else j += k + ;
} else {
if (t > ) j += k + ;
else i += k + ;
}
if (i == j) ++j;
k = ;
}
}
return min(i, j);
}
int main() {
// freopen("data.in", "r", stdin);
while (~scanf("%s", a)) {
int L = (int)strlen(a);
int p1 = minMaxRep(a, L, ) + ;
int p2 = minMaxRep(a, L, ) + ;
getNxt(a, L);
if (L % (L - nxt[L]) != ) {
printf("%d 1 %d 1\n", p1, p2);
} else {
int t = L / (L - nxt[L]);
printf("%d %d %d %d\n", p1, t, p2, t);
}
}
return ;
}
FZU 1901 Period II
For each prefix with length P of a given string S,ifS[i]=S[i+P] for i in [0..SIZE(S)-p-1],then the prefix is a “period” of S. We want to all the periodic prefixs.
需要输出满足period的p。 其实就是求所有的前后缀公共串。那么while(nxt[i]) 获取nxt[i],然后i=nxt[i];即可。
再而,那p和nxt[i]的关系又是什么呢?比如abcdxxxxabcd。对于最大的nxt[L]=4而言,即公共前缀为4的时候,i=L=12,第1个a要多少才到第2个a?距离刚好是L-nxt[L]
于是对于每个nxt[i],p就是L-nxt[i]。由于FZU挂了所以没交过,代码如下:
#include <cstdio>
#include <cstring>
using namespace std;
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define maxN 1000006
int nxt[maxN], L, cas, ans[maxN], cnt;
char a[maxN];
void getNxt(char *v, int m) {
nxt[] = -;
int i = , j = -;
while (i < m) {
if (j < || v[i] == v[j]) nxt[++i] = ++j;
else j = nxt[j];
}
} int main () {
// freopen("data.in", "r", stdin);
scanf("%d", &cas);
FOR(ca, , cas) {
cnt = ;
scanf("%s", a);
L = (int)strlen(a);
getNxt(a, L);
int cur = L;
while (nxt[cur])
ans[cnt++] = L - nxt[cur], cur = nxt[cur];
ans[cnt++] = L;
printf("Case #%d: %d\n", ca, cnt);
FOR(i, , cnt - ) printf("%d ", ans[i]);
puts("");
}
return ;
}
HDU 3613 待叙述, 比较值得品味
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std; #define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define ll long long
#define maxN 2000005 // cnt代表前缀
struct node {int sum, son[], cnt;} nd[maxN];
struct str{
int st, ed;
str() {}
str(int s, int e) : st(s), ed(e) {}
};
vector<str> v; int fg[maxN][], nxt[maxN], tot, n, pre, l;
char s[maxN], t[maxN]; void getNxt(char *a, int m) {
nxt[] = -;
int i = , j = -;
while (i < m) {
if (j < || a[i] == a[j]) nxt[++i] = ++j;
else j = nxt[j];
}
} // f 0前缀 1后缀 a是模式串
void KMP(int f, char *a, char *b, int m, int start) {
int i = , j = ;
while (i < m) {
if (j < || a[j] == b[i]) ++i, ++j;
else j = nxt[j];
}
int pre = j;
if (f == ) {
while (pre) {
fg[start + pre - ][] = ;
pre = nxt[pre];
}
} else {
while (pre) {
fg[start + l - pre][] = ;
pre = nxt[pre];
}
}
} void insert(int pre, char *a, int start, int L) {
FOR(i, , l - ) {
int cur = a[i] - 'a';
if (!nd[pre].son[cur]) {
++tot;
nd[pre].son[cur] = tot;
pre = tot;
} else pre = nd[pre].son[cur];
if (i + < L) nd[pre].cnt += fg[start + i + ][];
}
nd[pre].sum++;
} ll query(int start, int en, int pre) {
ll sym = , ans = , l = en - start;
FOR(i, start, en - ) {
int cur = t[i] - 'a';
if (nd[pre].son[cur]) {
pre = nd[pre].son[cur];
if (fg[start + l - (i - start + ) - ][] || i == en - )
ans += nd[pre].sum;
} else {
sym = ;
break;
}
}
if (sym) ans += nd[pre].cnt;
return ans;
} int main() {
// freopen("data.in", "r", stdin);
pre = ;
scanf("%d", &n);
ll ans = ;
while (n--) {
scanf("%d %s", &l, s + pre);
v.push_back(str(pre, pre + l));
FOR(i, pre, pre + l - ) t[i] = s[pre + l - (i - pre + )];
getNxt(s + pre, l);
KMP(, s + pre, t + pre, l, pre);
getNxt(t + pre, l);
KMP(, t + pre, s + pre, l, pre);
insert(, s + pre, pre, l);
pre += l;
}
FOR(i, , (int)v.size() - )
ans += query(v[i].st, v[i].ed, );
printf("%lld\n", ans);
}
hdu 4513 吉哥系列故事--完美队形II
问最长回文串,但要求左到中是非递减的,对称的,就是中到右是非递增的。
#include <cstdio>
#include <algorithm>
using namespace std;
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define maxN 1000005
int cas, n, T[maxN*];
int s1[maxN], s2[maxN*]; void manacher() {
s2[] = -, s2[] = -;
int j = ;
FOR(i, , n - )
s2[j++] = s1[i], s2[j++] = -; int id = , mx = ;
T[] = ;
FOR(i, , * n + ) {
T[i] = (i < mx) ? min(T[ * id - i], mx - i) : ;
while (s2[i - T[i]] == s2[i + T[i]]) {
if (s2[i + T[i]] != -) {
if (s2[i + T[i]] <= s2[i + T[i] - ]) T[i]++;
else break;
}
T[i]++;
}
if (i + T[i] > mx)
id = i, mx = i + T[i];
}
} int main () {
// freopen("data.in", "r", stdin);
scanf("%d", &cas);
while (cas--) {
scanf("%d", &n);
FOR(i, , n - )
scanf("%d", &s1[i]);
manacher();
int ans = ;
FOR(i, , * n + ) ans = max(ans, T[i]);
printf("%d\n", ans - );
}
return ;
}
51nod 1554 欧姆诺姆和项链
给数字n和k,n和k都∈[1,100000],再给一个字符串a,问前几个字符可构成这样的形式:ABAB...ABA,其中A有k+1个,B有k个,A和B可为空。前i个可以的时候输出1,否则0.
只有两种可能:SSSS..SS,或者是SSSS..ST。
假设为前者,那么循环节长度为i/(i-nxt[i]),设为x,又因为有k对AB,所以AB包含了x/k对S,剩下x%k个S就是A包含的S的个数,因B可以为空,所以x/k>=x%k即可。
假设为后者,其中T是S的一个前缀,即A,与前者一样,循环节长度为i/(i-nxt[i]),设为x,又因有k对AB,那么AB包含了x/k对S,剩下x%k个S ,以及单独的T,所以只要x/k<x%k即可。
#include <stdio.h>
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define maxN 1000009
int f[maxN];
char a[maxN]; void getf(char *x, int m) {
f[] = -;
int i = , j = -;
while (i < m) {
if (j < || x[i] == x[j]) f[++i] = ++j;
else j = f[j];
}
} int main () {
// freopen("data.in", "r", stdin);
int n, k;
scanf("%d%d%s", &n, &k, a);
getf(a, n);
FOR(i, , n) {
int x = i / (i - f[i]);
if (i % (i - f[i])) {
if (x / k > x % k) printf("");
else printf("");
} else {
if (x / k >= x % k) printf("");
else printf("");
}
}
return ;
}
51nod 1277
给一个字符串,前缀有2个属性,长度和在字符串中出现的次数。
问:所有前缀中,长度*字符串出现的次数 最大值是多少?
g[i]记录长度为i的前缀出现的次数。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define ll long long
#define maxN 100005
char a[maxN];
int f[maxN], g[maxN]; void init(int m) {
f[] = -;
int i = , j = -;
while (i < m) {
if (j < || a[i] == a[j]) f[++i] = ++j;
else j = f[j];
}
} int main () {
// freopen("data.in", "r", stdin);
scanf("%s", a);
int m = (int)strlen(a);
init(m);
for (int i = m; i >= ; --i)
g[i]++, g[f[i]] += g[i];
ll ans = ;
for (ll i = ; i <= m; ++i)
ans = max(i * g[i], ans);
printf("%lld\n", ans);
return ;
}
KMP、扩展KMP、Manacher习题的更多相关文章
- 字符串匹配—KMP 扩展KMP Manacher
kuangbin字符串专题传送门--http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#overview 算法模板: KMP: ; ...
- Kuangbin 带你飞 KMP扩展KMP Manacher
首先是几份模版 KMP void kmp_pre(char x[],int m,int fail[]) { int i,j; j = fail[] = -; i = ; while (i < m ...
- ACM之路(12)—— KMP & 扩展KMP & Manacher
最近做完了kuangbin的一套关于kmp的题目(除了一道字典树的不会,因为还没学字典树所以先放放),做个总结.(kuangbin题目的链接:http://acm.hust.edu.cn/vjudge ...
- 【string】KMP, 扩展KMP,trie,SA,ACAM,SAM,最小表示法
[KMP] 学习KMP,我们先要知道KMP是干什么的. KMP?KMPLAYER?看**? 正如AC自动机,KMP为什么要叫KMP是因为它是由三个人共同研究得到的- .- 啊跑题了. KMP就是给出一 ...
- kmp&扩展kmp
kmp: KMP的主要目的是求B是不是A的子串,以及若是,B在A中所有出现的位置 写的很详细的大佬的博客:http://www.matrix67.com/blog/archives/115 模板: / ...
- kuangbin专题十六 KMP&&扩展KMP HDU2609 How many (最小字符串表示法)
Give you n ( n < 10000) necklaces ,the length of necklace will not large than 100,tell me How man ...
- hdu 4300 Clairewd’s message(kmp/扩展kmp)
题意:真难懂.. 给出26个英文字母的加密表,明文中的'a'会转为加密表中的第一个字母,'b'转为第二个,...依次类推. 然后第二行是一个字符串(str1),形式是密文+明文,其中密文一定完整,而明 ...
- [kuangbin带你飞]专题十六 KMP & 扩展KMP & Manacher 题解报告
来刷kuangbin字符串了,字符串处理在ACM中是很重要的,一般比赛都会都1——2道有关字符串处理的题目,而且不会很难的那种,大多数时候都是用到一些KMP的性质或者找规律. 点击标题可跳转至VJ比赛 ...
- kuangbin专题十六 KMP&&扩展KMP HDU3613 Best Reward(前缀和+manacher or ekmp)
After an uphill battle, General Li won a great victory. Now the head of state decide to reward him w ...
- kuangbin专题十六 KMP&&扩展KMP HDU2328 Corporate Identity
Beside other services, ACM helps companies to clearly state their “corporate identity”, which includ ...
随机推荐
- [转]NodeBB 环境搭建
本文转自:https://my.oschina.net/pauli/blog/198405 摘要: Windows 7 下面 NodeBB (https://nodebb.org/)环境搭建 ...
- CPU简单科普
CPU简单科普 本文仅限于对小白科普. 误解一:CPU使用率和硬盘使用率一样. 误解二:一台电脑只有一个CPU. 误解三:CPU的核数,就是CPU的数量. 误解三:CPU主频越高越厉害:CPU核数越多 ...
- PHP开发环境安装说明书
php安装说明书 需要安装包可以拿U盘找技术--小豪拷贝. 一.安装对象和安装顺序 0 vcredist_x64.exe(Microsoft Visual C++ 运行时文件和操作系统组件) 1 ...
- TFS 打得你措手不及!TF53001:管理员已取消数据库操作
心塞.公司TFS突然挂了.签入获取 一直报 TF53001:管理员已取消数据库操作.公司开发部开发进度一下就受阻了.刚好有时关键时期. 在 老总的帮助下根据搜到的资料 .搞定了这个问题!问题出在数据库 ...
- 【代码笔记】iOS-MBProgressHUD+MJ
一,效果图. 二,工程图. 三,代码. ViewController.m #import "ViewController.h" #import "MBProgressHU ...
- 高性能JavaScript(加载和执行)
当浏览器遇到 <script> 标签时,它是没办法知道 JavaScript 是否会向DOM中添加内容或引入其他元素,甚至关闭某一个标签.因此这个时候浏览器就会停止处理页面,先执行Java ...
- img,a,锚链接,超链接
1.图片标签:img,单标签 图片属性: src(source): 图片的来源(路径),可以放置本地图片,也可以放网上的图片的url地址 title: 当鼠标停留在图片上的时候,显示提示的文字 alt ...
- JavaSE——序列化和反序列化
序列化: 序列化对应写的操作.(读与写都是站在应用的角度上) 序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化.可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间 ...
- <Android 基础(三十一)> ObjectAnimator
简介 ObjectAnimator,是ValueAnimator的子类,支持利用目标视图的属性来实现动画效果.构造ObjectAnimator的时候,将会提取对应的参数来定义动画对象和对象属性.合适的 ...
- SD从零开始57-58,第三方订单处理,跨公司销售
[原创] SD从零开始57 第三方订单处理流程 第三方订单处理的流程Processes for Third-Party Order Processing 客户的采购订单首先在你公司的一个销售组织作为一 ...