luogu3856 [TJOI2008]公共子串

http://www.elijahqi.win/archives/560
题目描述

一个字符串的子串是在这个串基础上去掉0个或者若干个字符所形成的,例如abc, aa和abbc都是字符串aabbcc的子串,而aba不是。 现给你三个字符串,请问他们三个共同含有多少种子串(不算空串)?

注意: 有些相同的公共子串尽管出现在不同的位置,但仍算1种,详见样例。

输入输出格式

输入格式:

每组测试数据只有3行,每行都是一个只包含有小写字母的字符串。

输出格式:

输出3个字符串共有的公共子串种类数。

输入输出样例

输入样例#1:

apartment
apache
approach
输出样例#1:

6
说明

3个字符串共有的公共子串有: “a”, “p”, “ap”, “pa”, “aa”, “apa”。 其中子串 “a” 有多个,但由于统计的是公共子串种类,所以 “a” 只算1种子串。

100%的数据中,字符串的长度不超过100。字符串中只含有小写字母。

在所有数据中,答案均小于2^30。

方案数可能超int。这题坑了好久。f[i][j][k]表示s1前i个字母,s2前j个字母,s3前k个字母所包含的本质不同的公共子串的个数。为了避免重复,我们每次做i,j,k时都重新统计所有的本质不同的公共子串。怎么统计呢?枚举公共子串的结尾为a…z,假设当前枚举a,找到目前为止的每一个串的最后一个a的位置,为了避免重复,我们把所有满足以a结尾的公共子串的结尾都当做是这个a,则个数为f[ii-1][jj-1][kk-1]+1。每次都重新统计,最后答案就是f[n1][n2][n3]。

举个例子 :要求f[i][j][k] 枚举b时 找到最后一次出现b的位置 f[i’][j’][k’] 那么 前面有的bb aa ab cd等串

都都会加上现在的b形成新的串 bbb也是一个新的子串 那么c d 等结尾的就是后面继续循环的时候可以找到

#include<cstdio>
#include<cstring>
#define N 110
long long data[N][N][N];
char a[N],b[N],c[N];
int last1[N][30],last2[N][30],last3[N][30];
void f(int i,int j,int k){
   long long ans=0;
   //data[i][j][k]=0;
    for (int z=0;z<=25;++z){
        int x1=last1[i][z],x2=last2[j][z],x3=last3[k][z];
        if (x1==0||x2==0||x3==0) continue;
        if (data[x1-1][x2-1][x3-1]==-1) f(x1-1,x2-1,x3-1);
        ans+=data[x1-1][x2-1][x3-1]+1;    
    }
    data[i][j][k]=ans;
}
int main(){
   // freopen("3856.in","r",stdin);
    scanf("%s%s%s",a+1,b+1,c+1);
    int n=strlen(a+1),m=strlen(b+1),k=strlen(c+1);
    for (int i=1;i<=n;++i) 
        for (int j=0;j<26;++j) {last1[i][j]=last1[i-1][j];if(a[i]=='a'+j) last1[i][j]=i;}
    for (int i=1;i<=m;++i)
        for (int j=0;j<26;++j){ last2[i][j]=last2[i-1][j];if (b[i]=='a'+j) last2[i][j]=i;}
    for (int i=1;i<=k;++i)
        for (int j=0;j<26;++j){ last3[i][j]=last3[i-1][j];if (c[i]=='a'+j) last3[i][j]=i;}
    memset(data,0xff,sizeof(data));
    //data[0][0][0]=1;
    f(n,m,k);
    printf("%lld",data[n][m][k]);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值