next[i]:求模式串中前缀和后缀的最长公共前缀
即对于[1,i]来说,求最大的x,使s[1,x]等于s[i-x+1,i]
以hdu1711为例,
统计a串中第一个出现b串的起始下标,
即若第一个出现b串的区间是[l,r],输出l
- #include
- #include
- #include
- using namespace std;
- int a[1000005],b[10005];
- int n,m;
- int nex[10005];
- void kmppre(){
- int i=0,j=nex[0]=-1;
- while(i
- while(j!=-1&&b[i]!=b[j])j=nex[j];
- nex[++i]=++j;
- }
- }
- int kmpcount(){
- int i,j,ans=0;
- kmppre();
- i=j=0;
- while(i
- while(j!=-1&&a[i]!=b[j])j=nex[j];
- i++;j++;
- if(j==m)return i-j+1;
- }
- return -1;
- }
- int main(){
- int T;
- scanf("%d",&T);
- while(T--){
- scanf("%d%d",&n,&m);
- for(int i=0;i
scanf("%d",&a[i]); - for(int i=0;i
scanf("%d",&b[i]); - printf("%d\n",kmpcount());
- }
- return 0;
- }
最小表示法与最大表示法
最小表示法:
对于串bca来说,其循环串abc,bca,cab中字典序最小的那个,串abc,即为最小表示
最大表示法同理
hdu3374,用kmp求一个串的最小表示法
- //一个串在表示法里出现了几次 等于这个串的循环节在串中出现的次数
- //考虑一串珠子 往后转循环节个长度 还能得到一个与原串相同的串 这就是循环节的定义
- //即循环节出现的次数 即为表示法中出现的次数
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- const int INF=0x3f3f3f3f;
- const int MOD=1e9+7;
- const double eps=1e-7;
- typedef long long ll;
- #define vi vector
- #define si set
- #define pii pair
- #define pi acos(-1.0)
- #define pb push_back
- #define mp make_pair
- #define lowbit(x) (x&(-x))
- #define sci(x) scanf("%d",&(x))
- #define scll(x) scanf("%lld",&(x))
- #define sclf(x) scanf("%lf",&(x))
- #define pri(x) printf("%d",(x))
- #define rep(i,j,k) for(int i=j;i<=k;++i)
- #define per(i,j,k) for(int i=j;i>=k;--i)
- #define mem(a,b) memset(a,b,sizeof(a))
- using namespace std;
- string s;
- int net[1000005];
- int getminmax(int flag,string s)//min是0 max是1 注意flag的应用
- {
- int n=s.size();
- int i=0,j=1,k=0,t;
- while(i
- {
- t=s[(i+k)%n]-s[(j+k)%n];
- if(!t)k++;
- else
- {
- if(!flag)t>0?i+=k+1:j+=k+1;
- else t>0?j+=k+1:i+=k+1;
- if(i==j)j++;
- k=0;
- }
- }
- return i
- }
- void kmppre(string s)
- {
- int i,j,n=s.size();
- i=0,j=net[0]=-1;
- while(i
- {
- while(j!=-1&&s[i]!=s[j])j=net[j];
- net[++i]=++j;
- }
- }
- void init(int &t)
- {
- mem(net,0);
- t=1;
- }
- int main()
- {
- int len,minn,maxx,t;
- while(cin>>s)
- {
- init(t);
- kmppre(s);
- len=s.size();
- minn=getminmax(0,s);//求最小表示法
- maxx=getminmax(1,s);//求最大表示法
- if(len%(len-net[len])==0)t=len/(len-net[len]);
- printf("%d %d %d %d\n",1+minn,t,1+maxx,t);//minn的pos和maxx的pos
- }
- return 0;
- }
循环节
设m为串长,则m-next[m]是一个循环节,
m-next[next[m]]也是循环节,以此类推…
可以不断跳next数组获取到所有循环节
这些循环节并不是都能整除m,
能整除m的最短循环节最为常用
扩展kmp(Z函数)
next[i]:求模式串中前缀和后缀的最长公共前缀
即对于[1,i]来说,求最大的x,使s[1,x]等于s[i-x+1,i]
extend[i]: 求文本串中以i开始的一个后缀,和模式串中的前缀的最长公共前缀
即,对于文本串t[1,m]和模式串s[1,n]来说,
求最大的x,使得t[i,i+x-1]等于s[1,x]
替代方案
kmp可以取巧替代exkmp,并且实际效果比kmp快
令t=s+'#'+t,即模式串在最前,中间用未出现的字符分隔,文本串在最后
则数组后半部分的next[i]即为所求
例题
以poj3080为例,
多个字符串,问你这几个字符串的最长公共子串是哪个,
如果有多个,输出字典序最大的那个,
如果最长的公共子串长度小于3,输出no significant commonalities
其实有点强行用exkmp的意思,暴力枚举每个串判是否在其他的串中存在,kmp也可以
- #include
- #include
- #include
- #include
- #include
- #include
- #define sci(x) scanf("%d",&(x))
- #define scs(x) scanf("%s",(x))
- #define scll(x) scanf("%lld",&(x))
- #define sclf(x) scanf("%lf",&(x))
- #define rep(i,j,k) for(int i=j;i<=k;++i)
- #define mem(a,b) memset(a,b,sizeof(a))
- using namespace std;
- char a[15][65];
- int net[65],ex[65];
- char s1[65],s2[65],tmp[65];
- void extkmppre(char s[])
- {
- int i=0,j,pos,len=strlen(s);
- net[0]=len;
- while(i+1
1])i++; - net[1]=i,pos=1;
- rep(i,2,len-1)
- {
- if(net[i-pos]+i
- {
- net[i]=net[i-pos];
- }
- else
- {
- j=net[pos]+pos-i;
- if(j<0)j=0;
- while(i+j
- net[i]=j,pos=i;
- }
- }
- }
- bool extkmp(char s1[],char s2[])
- {
- int i=0,j,pos,l1=strlen(s1),l2=strlen(s2);
- extkmppre(s2);
- while(i
- ex[0]=i,pos=0;
- if(ex[0]==l2)return 1;
- rep(i,1,l1-1)
- {
- if(net[i-pos]+i
- {
- ex[i]=net[i-pos];
- }
- else
- {
- j=ex[pos]+pos-i;
- if(j<0)j=0;
- while(i+j
- ex[i]=j,pos=i;
- }
- if(ex[i]==l2)return 1;
- }
- return 0;
- }
- void cpy(char a[],char b[],int l,int r)
- {
- rep(i,l,r)a[i-l]=b[i];
- a[r-l+1]='\0';
- }
- int main()
- {
- int t;
- sci(t);
- while(t--)
- {
- mem(a,0);
- mem(tmp,0);
- int m,maxlen=0,l,r;
- sci(m);
- rep(i,0,m-1)
- scs(a[i]);
- rep(i,0,59)
- {
- rep(j,i+2,59)
- {
- if(j-i+1
continue;//剪枝 - bool flag=1;
- cpy(tmp,a[0],i,j);
- rep(k,1,m-1)
- {
- if(extkmp(a[k],tmp))continue;
- flag=0;
- break;
- }
- if(flag)
- {
- if(j-i+1>maxlen)
- {
- maxlen=j-i+1;
- l=i,r=j;
- }
- else if(j-i+1==maxlen)
- {
- rep(k,0,maxlen-1)
- {
- if(a[0][k+i]>a[0][k+l])break;
- {
- l=i,r=j;
- break;
- }
- }
- }
- }
- }
- }
- if(maxlen<3)puts("no significant commonalities");
- else
- {
- cpy(tmp,a[0],l,r);
- printf("%s\n",tmp);
- }
- }
- return 0;
- }
-
相关阅读:
3-egg-TS-通用后端管理注册系统-图形验证码
串行原理编程,中文编程工具中的串行构件,串行连接操作简单
ELK:开源搜索与分析技术栈(2)
UDP内核发包流程
位逻辑运算符
MySQL的索引原理
高效Go编程: encoding/csv标准库深度解析
JVM的垃圾回收机制
香港日本服务器好机推荐CN2三网直连高速又稳定
关于ASPICE 4.0评估师资质更新的说明-亚远景科技
-
原文地址:https://blog.csdn.net/Code92007/article/details/127991986