您的位置:首页 > 其它

ZOJ3587 Marlon's String KMP技巧处理

2016-03-26 14:10 381 查看
题意:给定一个 T 串,一个 S 串,问由 S 串中的两个子串组成 T 串有多少种方式?

思路:这道题让我搞了好久,举个例子,将T串分成任意两段,那么必然是从中间断开的,即我们就需要在S中寻找和T的前半段匹配的子串数量,记录在V1数组中,和T的后半段匹配的子串的数量,记录在V2数组中,最后求出 V1[1]*V2[strlen(T)-1]+V1[2]*V2[strlen(T)-2]+……+V1[strlen(T)-1]*V2[1] 即可。

我的做法是将两个字符串连接,T在前S在后,同时在连接处加一个不相关字符‘*’,这样做是为了避免next数组在S中寻找子串的时候也计算上了T串的字符,计算前半段匹配直接遍历next数组就可以了计算后半段则是将两字符串均翻转后连接。,之后通过next数组在其中寻找子串。但这样做还是有可能遗漏一些子串,比如next只会记录最长匹配,但是往往这些最长匹配中也包含了一些次长的匹配,我们可以通过next跳跃将这些遗漏处补充上(跳跃方式很简单 看代码注释),这样就可以得到完整的V1和V2数组了。最后千万别忘了 V1,V2和sum都是long long 才行哦。

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int maxn=100005;
int NEXT[maxn<<1];
char s[maxn];
char t[maxn];
char st[maxn<<1];
long long v1[maxn];
long long v2[maxn];

void get_NEXT(char *p){
int k=-1;
int j=0;
int n=strlen(p);
NEXT[0]=-1;
while(j<n){
if(k==-1||p[j]==p[k]){
k++;
j++;
NEXT[j]=k;
}
else k=NEXT[k];
}
}

int main()
{
ios::sync_with_stdio(false);
int T;
cin>>T;
while(T--){
memset(st,0,sizeof(st));
memset(v1,0,sizeof(v1));
memset(v2,0,sizeof(v2));
cin>>s>>t;
strcat(st,t);
st[strlen(st)]='*';
strcat(st,s);
get_NEXT(st);
int ns=strlen(s);
int nt=strlen(t);
int n=strlen(st);
for(int i=nt+2;i<=n;i++){
v1[NEXT[i]]++;
}
for(int i=nt;i>1;i--){
//next数组跳跃补充遗漏。即最大匹配包含了次大匹配

v1[NEXT[i]]+=v1[i];
}
memset(st,0,sizeof(st));
int Count=0;
for(int i=nt-1;i>=0;i--){
st[Count++]=t[i];
}
st[Count++]='*';
for(int i=ns-1;i>=0;i--){
st[Count++]=s[i];
}
get_NEXT(st);
for(int i=nt+2;i<=n;i++){
v2[NEXT[i]]++;
}
for(int i=nt;i>1;i--){
//next数组跳跃补充遗漏。即最大匹配包含了次大匹配
v2[NEXT[i]]+=v2[i];
}
long long sum=0;
for(int i=1;i<nt;i++){
sum += v1[i]*v2[nt-i];
}
cout<<sum<<endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: