ZOJ3587 Marlon's String KMP技巧处理

题意:给定一个 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 
#include 
#include 

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(jif(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;icout<return 0;
}

你可能感兴趣的:(ACM-数据结构)