字符串算法(KMP+MANACHER+EX_KMP)总结
2016-08-08 19:02
477 查看
字符串算法:
1、 KMP算法
2、 MANACHER算法
3、 EX_KMP算法
KMP算法代码:
求Next数组源代码:
KMP算法匹配源代码:
MANACHER算法代码:
EX_KMP算法代码:(与MANACHER相似)
计算Next数组源代码:
与S串匹配源代码:
字符串读入:
一般情况使用KMP、MANACHER算法前,字符串从STRING[1]开始记录,EX_KMP从STRING[0]开始记录。
KMP的应用:
1、 求字符串T在S中出现的次数
poj3461 Oulipo
做法:直接用KMP进行匹配。
2、求字符串中的循环节
poj2185 Milking Grid
做法:行方向和列方向分别找出最小循环节长R和C,面积S就是R*C。
HDU3746 Cyclic Nacklace
做法:
若有完整的循环节如图中例1,则无需添加。
否则就有残缺的循环节如图中例2,则求出补齐所需的花费。
具体代码片段如下:
MANACHER的应用
求回文长度
两种情况:
1、如字符串ababac 最长回文就是以S[3]为中点,长度为5的回文串。
2、如字符串babbac 最长回文就是以S[3]和S[4]两个字符为中点,长度为4的回文串。
情况2可以通过预处理转换为情况1。方法:每两个字符间插入一个从未在原串出现过的相同字符,如’#’、’|’、’*’…同时为了防止数组越界,头尾分别加入两个不同的奇怪字符。
用此方法处理的字符串babbac为 @#b#a#b#b#a#c#!
Len=Len*2+3
回文长度为ans[i]-1 (不包括添加的字符,可证明)
HDU3068 最长回文
做法:直接做MANACHER。
HDU3613 Best Reward
做法:做MANACHER同时判断,若回文串包括左端点,则记录pl[ans[i]-1]=true 表示前ans[i]-1个字符组成回文串。 若包括右端点,则记录pr[ans[i]-1]=true 表示后ans[i]-1个字符组成回文串。
具体代码片段如下:
EX_KMP的应用
给出一个长度n的字符串S[0..n-1]
和一个长度m的字符串T[0..n-1]
问S的哪个后缀和T具有最长的公共前缀
HDU4333 Revolving Digits
做法:可以把数字再复制一遍,用EX_KMP匹配,求出每一个后缀与原数相同的数字个数Next[i],如果大于原长则相同,否则比较下一个不同的数字。
注意:要求比较的是不同的数字,如果有循环节,则需要除去循环节个数(KMP)。
附三个完整模板:
1、 KMP算法
2、 MANACHER算法
3、 EX_KMP算法
KMP算法代码:
求Next数组源代码:
next[0]=next[1]=0; k=0; for (i=2;i<=m;i++) { while (k>0&&T[i]!=T[k+1]) k=next[k]; if (T[i]==T[k+1]) k++; next[i]=k; }
KMP算法匹配源代码:
k=0; //k表示S串当前位匹配到T串第k位 for (i=1;i<=n;i++) { while (k>0&&S[i]!=T[k+1]) k=next[k]; //跳到可匹配处 if (S[i]==T[k+1]) k++; if (k==m) { k=next[k]; ans++; } } //ans表示出现次数
MANACHER算法代码:
ans[0]=ans[1]=0; p=1; for (i=2;i<=n;i++) //枚举回文中点 { ans[i]=max(0, min(ans[2*p-i],p+ans[p]-i) ); //不超出最右端就直接取 while (S[i-ans[i]]==S[i+ans[i]]) ans[i]++; //暴力扩展 if (i+ans[i]>p+ans[p]) p=i; } //更新最右断点
EX_KMP算法代码:(与MANACHER相似)
计算Next数组源代码:
next[0]=m; for (i=0;T[i]==T[i+1];i++); next[1]=i; p=1; for (i=2;i<m;i++) { u=p+next[p]; //u为最右端位置 if (i+next[i-p]<u) next[i]=next[i-p];//如果答案可以直接取 else { //如果答案需要探索 for (j=max(u-i,0);T[i+j]==T[j];j++); next[i]=j; p=i; } }
与S串匹配源代码:
for (i=0;i<m&&S[i]==T[i];i++); ex[0]=i; p=0; for (i=1;i<n;i++) { u=p+ex[p]; //u为最右端位置 if (i+next[i-p]<u) ex[i]=next[i-p]; //如果答案可以直接取得 else { //如果答案需要探索 for (j=max(u-i,0);j<=m&&S[i+j]==T[j];j++); ex[i]=j; p=i; } }
字符串读入:
一般情况使用KMP、MANACHER算法前,字符串从STRING[1]开始记录,EX_KMP从STRING[0]开始记录。
KMP的应用:
1、 求字符串T在S中出现的次数
poj3461 Oulipo
做法:直接用KMP进行匹配。
2、求字符串中的循环节
poj2185 Milking Grid
做法:行方向和列方向分别找出最小循环节长R和C,面积S就是R*C。
HDU3746 Cyclic Nacklace
做法:
若有完整的循环节如图中例1,则无需添加。
否则就有残缺的循环节如图中例2,则求出补齐所需的花费。
具体代码片段如下:
int cir=Len-next[Len]; //循环节长 if (Len%cir==0&&cir!=Len) //是否完整 printf("0\n"); else printf("%d\n",cir-Len%cir);
MANACHER的应用
求回文长度
两种情况:
1、如字符串ababac 最长回文就是以S[3]为中点,长度为5的回文串。
2、如字符串babbac 最长回文就是以S[3]和S[4]两个字符为中点,长度为4的回文串。
情况2可以通过预处理转换为情况1。方法:每两个字符间插入一个从未在原串出现过的相同字符,如’#’、’|’、’*’…同时为了防止数组越界,头尾分别加入两个不同的奇怪字符。
用此方法处理的字符串babbac为 @#b#a#b#b#a#c#!
Len=Len*2+3
回文长度为ans[i]-1 (不包括添加的字符,可证明)
HDU3068 最长回文
做法:直接做MANACHER。
HDU3613 Best Reward
做法:做MANACHER同时判断,若回文串包括左端点,则记录pl[ans[i]-1]=true 表示前ans[i]-1个字符组成回文串。 若包括右端点,则记录pr[ans[i]-1]=true 表示后ans[i]-1个字符组成回文串。
具体代码片段如下:
if (i-ans[i]==1) pl[ans[i]-1]=1; if (i+ans[i]==Len) pr[ans[i]-1]=1;
EX_KMP的应用
给出一个长度n的字符串S[0..n-1]
和一个长度m的字符串T[0..n-1]
问S的哪个后缀和T具有最长的公共前缀
HDU4333 Revolving Digits
做法:可以把数字再复制一遍,用EX_KMP匹配,求出每一个后缀与原数相同的数字个数Next[i],如果大于原长则相同,否则比较下一个不同的数字。
注意:要求比较的是不同的数字,如果有循环节,则需要除去循环节个数(KMP)。
附三个完整模板:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; int kmp() { char t[100],s[100]; scanf("%s%s",t+1,s+1); int next[100],ans=0,k; int len1=strlen(t+1),len2=strlen(s+1); next[0]=next[1]=k=0; for (int i=2;i<=len1;i++) { while (k>0&&t[k+1]!=t[i]) k=next[k]; if (t[k+1]==t[i]) k++; next[i]=k; } k=0; for (int i=1;i<=len2;i++) { while (k>0&&t[k+1]!=s[i]) k=next[k]; if (t[k+1]==s[i]) k++; if (k==len1) { ans++; k=next[k]; } } printf("%d",ans); return 0; } int manacher() { char t[100],s[100]; scanf("%s",t+1); int len=2; s[1]='@'; s[2]='#'; for (int i=1;i<strlen(t);i++) { s[++len]=t[i]; s[++len]='#'; } s[++len]='!'; int ans[100],p=1; memset(ans,0,sizeof(ans)); for (int i=2;i<=len;i++) { ans[i]=max(0,min(ans[2*p-i],p+ans[p]-i)); while (s[i+ans[i]]==s[i-ans[i]]) ans[i]++; if (ans[i]+i>ans[p]+p) p=i; } for (int i=2;i<=len-1;i++) printf("%c%d ",s[i],ans[i]-1); return 0; } int Ex_kmp() { char t[100],s[100]; scanf("%s%s",t,s); int next[100],ex[100]; int len1=strlen(t),len2=strlen(s); next[0]=len1; for (next[1]=0;t[next[1]]==t[next[1]+1];next[1]++); int p=1; for (int i=2;i<len1;i++) { int u=p+next[p]; if (i+next[i-p]<u) next[i]=next[i-p]; else { int j; for (j=max(u-i,0);t[i+j]==t[j];j++); next[i]=j; p=i; } } int i; for (i=0;i<=len1&&s[i]==t[i];i++); ex[0]=i; p=0; for (i=1;i<len2;i++) { int u=p+ex[p]; if (i+next[i-p]<u) ex[i]=next[i-p]; else { int j; for (j=max(u-i,0);t[j]==s[i+j];j++); ex[i]=j; p=i; } } for (int i=0;i<len2;i++) printf("%d ",ex[i]); return 0; } int main() { //kmp(); //manacher(); //Ex_kmp(); return 0; }
相关文章推荐
- 字符串查找算法总结(暴力匹配、KMP 算法、Boyer-Moore 算法和 Sunday 算法)
- 字符串查找算法总结(暴力匹配、KMP 算法、Boyer-Moore 算法和 Sunday 算法)
- 字符串查找算法总结(暴力匹配、KMP 算法、Boyer-Moore 算法和 Sunday 算法)
- 字符串查找算法总结(暴力匹配、KMP 算法、Boyer-Moore 算法和 Sunday 算法)
- 算法基础-->字符串(LCS,KMP,Huffman,Manacher)
- KMP字符串模式匹配算法
- 字符串比较算法总结以及朴素字符串比较的实现
- 对一道“写一个算法实现字符串逆序存储,要求不另设串存储空间.”题目的总结!
- 六之再续:KMP算法之总结篇(12.09修订,必懂KMP)
- KMP 算法并非字符串查找的优化
- 字符串模式匹配算法——BM、Horspool、Sunday、KMP、KR、AC算法一网打尽
- 六之再续:KMP算法之总结篇(必懂KMP)
- 一种比KMP更优的字符串模式匹配算法
- 字符串模式匹配算法——BM、Horspool、Sunday、KMP、KR、AC算法一网打尽
- 六之再续:KMP算法之总结篇(12.09修订,必懂KMP)
- 算法:字符串的KMP模式匹配
- KMP字符串模式匹配算法
- 字符串模式匹配(所谓的kmp)算法
- 字符串的模式匹配算法 KMP
- KMP字符串模式匹配算法最详解