现在的位置: 首页 > 综合 > 正文

ZOJ 3587 Marlon’s String 扩展KMP

2018年04月24日 ⁄ 综合 ⁄ 共 2091字 ⁄ 字号 评论关闭

链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3587

题意:给出两个字符串S和T,S,T<=100000.拿出S的两个子串(可以重叠),将两个子串连接起来成为字符串T的方法有多少种。

思路:用扩展KMP求出S的从每位开始的子串与T的公共前缀,再将两个子串翻转,再用扩展KMP求出S反的从每位开始的子串与T反的公共前缀。找出其中和为T子串长度的S公共前缀和S反的公共前缀的数量,相乘为结果。

代码:

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <ctype.h>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
#define eps 1e-8
#define INF 0x7fffffff
#define maxn 10005
#define PI acos(-1.0)
#define seed 31//131,1313
typedef long long LL;
typedef unsigned long long ULL;
using namespace std;
const int N = 101010;
int next_a[N],extand_a[N],next_c[N],extand_c[N];
void getnext(char *T,int *next,int *extand) // next[i]: 以第i位置开始的子串 与 T的公共前缀
{
    int i,length = strlen(T);
    next[0] = length;
    for(i = 0; i<length-1 && T[i]==T[i+1]; i++);
    next[1] = i;
    int a = 1;
    for(int k = 2; k < length; k++)
    {
        int p = a+next[a]-1, L = next[k-a];
        if( (k-1)+L >= p )
        {
            int j = (p-k+1)>0? (p-k+1) : 0;
            while(k+j<length && T[k+j]==T[j]) j++;// 枚举(p+1,length) 与(p-k+1,length) 区间比较
            next[k] = j, a = k;
        }
        else next[k] = L;
    }
}
void getextand(char *S,char *T,int *next,int *extand) //s是母串,t是模式串
{
    memset(next,0,sizeof(next));
    getnext(T,next,extand);
    int Slen = strlen(S), Tlen = strlen(T), a = 0;
    int MinLen = Slen>Tlen?Tlen:Slen;
    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 = next[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;
    }
}
char a[100005],b[100005],c[100005],d[100005];
LL t_a[100005],t_c[100005];
void init()
{
    memset(t_a,0,sizeof(t_a));
    memset(t_c,0,sizeof(t_c));
}
int main()
{
    //freopen("1.txt","r",stdin);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        init();
        scanf("%s",a);
        scanf("%s",b);
        int len_a=strlen(a);
        for(int i=0;i<len_a;i++)
            c[i]=a[len_a-1-i];
        c[len_a]='\0';
        int len_b=strlen(b);
        for(int i=0;i<len_b;i++)
            d[i]=b[len_b-1-i];
        d[len_b]='\0';
        getextand(a,b,next_a,extand_a);
        getextand(c,d,next_c,extand_c);
        for(int i=0;i<len_a;i++)
        {
            t_a[extand_a[i]]++;
            t_c[extand_c[i]]++;
        }
        for(int i=len_a-1;i>=1;i--)
        {
            t_a[i]+=t_a[i+1];
            t_c[i]+=t_c[i+1];
        }
        LL ans=0;
        for(int i=1;i<len_b;i++)
        {
            ans+=t_a[i]*t_c[len_b-i];
        }
        printf("%lld\n",ans);
    }
    return 0;
}

抱歉!评论已关闭.