字符串(1)---KMP & 扩展KMP & Manacher

时间:2023-01-03 16:29:02

练习:点击打开链接

字符串也是ACM中的重头戏,基本内容有KMP ,扩展KMP, Manacher ,AC自动机,后缀数组,后缀自动机.按照专题来做共分三部分. LCS LIS LCIS不知道算不算....点击打开链接

小技巧:匹配问题不区分大小写,则将其全部转为小写.

暴力匹配: 用strstr函数就能解决       I M N Z(枚举长度 三份)

一.KMP算法

解决单一模式串匹配问题.

利用失配后的nxt数组减少移位,达到O(n)级别.资料自行百度.

延展:

1.求最小循环节 点击打开链接   就是len-nxt[len]                D E(这个题目意思是i为止可以表示成循环的字符) F G

2.求各个循环节  P(先求出最小循环节,然后进入nxtval[i]再求最小循环节累加就是第二小依次循环)

3.字符串最大最小表示法 O(最大最小 循环长度就是最小循环节) P(都转为最小 map存放)

4.模式串成功匹配次数(可以有重叠) 匹配成功后j=nxt[j]     B

5.模式串成功匹配次数(不能有重叠) 匹配成功后j=0          C

6.既是前缀又是后缀    用nextval递归往后  H

7.一个串的最长前缀另一个串的最长后缀,连起来求nxt 完了判断最长的不要大于字符串长就行

8.各种前缀可匹配的次数 自左至右计算nxt[]的dp[] ,dp[i]=dp[nxt[i]]+1 ans+=dp[i]

9.


二.扩展KMP

求extand[] 表示以i起始的后缀串与模式串匹配的长度.nxt[] 表示以i起始模式串的后缀串与模式串匹配的长度  点击打开链接

1.利用nxt数组求最长后缀串匹配长度 裸EXKMP    L


三.最长回文

一个串中最长的连续回文.p[i] 以i为中点的最长回文数.点击打开链接

U V W X 

其中V是吉哥系列2要求连续 规模10^5,有个要求是大于等于,所以加个限制条件就行 系列1使用的是LCS,不要求连续规模10^2 

不要求连续的回文使用转置再求LCS (HDU1513 点击打开链接)


///核心思想是利用之前已经"匹配"信息减少j移动
///EXKMP方法是记录一个最远匹配位置
///KMP
void getnxt(){
int j = -1;
nxt[0] = -1;
for(int i = 0;i < M;){
if(j == -1 || s2[i] == s2[j]){
i++,j++;
if(s2[i] == s2[j]) nxt[i] = nxt[j];
else nxt[i] = j;
}
else j = nxt[j];
}
}
int KMP(){
getnxt();
int i = 0,j = 0;
while(i < N){ ///这个写错了,不是字符串\0的意思
if(j == -1 || s1[i] == s2[j])
i++,j++;
else j = nxt[j];
if(j==M) return i-M+1;
}
return -1;
}
///EXKMP
void Getnxt(const char *T){
int len=strlen(T),a=0;
nxt[0]=len;
while(a<len-1 && T[a]==T[a+1]) a++;
nxt[1]=a;
a=1;
for(int k=2;k<len;k++){
int p=a+nxt[a]-1,L=nxt[k-a];
if( (k-1)+L >= p){
int j = (p-k+1)>0 ? (p-k+1) : 0;
while(k+j<len && T[k+j]==T[j]) j++;
nxt[k]=j;
a=k;
}
else
nxt[k]=L;
}
}
void GetExtand(const char *S,const char *T){
Getnxt(T);
int slen=strlen(S),tlen=strlen(T),a=0;
int MinLen = slen < tlen ? slen : tlen;
while(a<MinLen && S[a]==T[a]) a++;
extand[0]=a;
a=0;
for(int k=1;k<slen;k++){
int p=a+extand[a]-1, L=nxt[k-a];
if( (k-1)+L >= p){
int j= (p-k+1) > 0 ? (p-k+1) : 0;
while(k+j<slen && j<tlen && S[k+j]==T[j]) j++;
extand[k]=j;
a=k;
}
else
extand[k]=L;
}
}



/**
最长回文子串,求的是连续.如果可以不连续则reserve再LCS
http://blog.csdn.net/xingyeyongheng/article/details/9310555
**/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 150000
char fst[maxn];
char sed[2*maxn+2];
int p[2*maxn+2];
int Manacher(){
int len = strlen(fst);
int maxlen =0,id=0;
for(int i = 0;i < len;i++){
sed[i*2+1] = '#';
sed[i*2+2] = fst[i];
}
sed[2*len+1] = '#';
sed[2*len+2] = 0;
sed[0] = '@';
len = 2*len+1;
for(int i = 2;i < len;i++){
if(p[id] + id > i) p[i] = min(p[2*id-i],p[id]+id-i);
else p[i] = 1;///公式化简一下 k=i-id i-k=2id-i
while(sed[i-p[i]] == sed[i+p[i]]) ++p[i];///害怕这里越界
if(id+p[id]<i+p[i]) id=i;///i+p[id] 这个写错了....我擦
maxlen = max(maxlen,p[i]);
}
return maxlen-1;
}

int main(){
while(~scanf("%s",fst))
printf("%d\n",Manacher());
return 0;
}