版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/85258735
之前想写多项式的各种算法,然后被FFT多项式求逆打自闭了。
临表涕零,不知所言。
就按我学的顺序来吧。
-
可求出对于每个点 ,以 为中心的最长回文串的右端点到i的总字符数 (即 为最长回文串的右端点)。
如果在字符串每两个字符间插入一个 字符,那么也可以计算长度为偶数的回文串(没有中心)
发现 如果在另一个回文串的右半部分,即存在 使得 并且 ,那么 (因为回文串的性质,在这个回文串中才满足)。
那么我们从小到大计算 ,保存 到 的最远的回文串的右端点 和其回文串中心 ,那么如果 那么就暴力增加 并验证,否则就让 然后再暴力增加 并验证。
发现每次暴力的成功增加都会导致 增大。。。。。。
所以只会暴力 次。
时间复杂度模板:luogu 3805
#include<bits/stdc++.h>
#define maxn 22000005
using namespace std;
int lc[maxn];
char s[maxn];
void manacher(char s[maxn],int lc[maxn])
{
int tot=0;
static char ns[maxn]={};
ns[tot] = '&' , ns[++tot] = '*'; // 首尾增加奇怪的字符,那么怎么扩展都会在边界时失败,省去了判断边界
for(int i=0,len=strlen(s);i<len;i++) ns[++tot] = '*' , ns[++tot] = s[i];
ns[++tot] = '*' , ns[++tot] = '%';
int mx = 0 , o = 0;//
for(int i=2;i<tot-1;i++)
{
if(i < mx) lc[i] = min(lc[2*o-i],mx-i);
else lc[i] = 1;
for(;ns[i-lc[i]] == ns[i+lc[i]];lc[i]++);
if(i+lc[i] > mx) mx = i + lc[i] , o = i;//实际取右端点+1更方便
}
}
int main()
{
scanf("%s",s);
manacher(s,lc);
printf("%d",*max_element(lc,lc+(strlen(s)<<1)+1)-1);
}
-
最大的特点就是 数组,其他的都是这个数组的用途而已。
为 最大的 满足字符串
这个 也可以像 一样利用之前的答案。
如果 那么
否则就看是否有 有就
一直算下去。
发现每次nxt最多增加1,减少至少减少1,那么总共就只会增加减少 次。
时间复杂度
而这个数组的主要用途是拿来求一个串A在另一个串B中出现了多少次的。
我们把A的 数组求出。
然后从小到大枚举i,维护以i结尾的B串最多匹配A串的前k位,发现这个可以用 优化,如果B[i] = A[k] 那么 i结尾可以匹配前k位,否则k=nxt[k]继续比较。。。。。 时匹配成功。
模板:luogu P3375
#define maxn 1000005
using namespace std;
char s1[maxn],s2[maxn];
void KMP(char s1[maxn],char s2[maxn])
{
static int nxt[maxn];
int n = strlen(s1) , m = strlen(s2);
nxt[0] = -1;
for(int i=0,j=-1;i<m;)
if(j==-1 || s2[i] == s2[j]) nxt[++i] = ++j;
else j = nxt[j];
for(int i=0,j=0;i<=n;i++,j++)
{
if(j == m) printf("%d\n",i-m+1) , j = nxt[j];
if(i<n) while(j!=-1 && s1[i]!=s2[j]) j = nxt[j];
}
for(int i=1;i<m;i++)
printf("%d ",nxt[i]);
printf("%d\n",nxt[m]);
}
int main()
{
scanf("%s\n%s",s1,s2);
KMP(s1,s2);
}
除此之外,nxt数组还有很多的妙用。
但和manacher一样,和别的套起来考就是一道好题
例【POJ 3167】Cow Patterns (KMP+树状数组)