题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4622
2 bbaba 5 3 4 2 2 2 5 2 4 1 4 baaba 5 3 3 3 4 1 4 3 5 5 5
3 1 7 5 8 1 3 8 5 1HintI won‘t do anything against hash because I am nice.Of course this problem has a solution that don‘t rely on hash.
给一个字符串,对每个字符串,有很多询问,询问给定区间不同子串的个数。
解题思路:
kmp+dp
普通的dp转移肯定超时。
sa[i][j]:表示以第j个字符开始能够往前最多的字符个数(假设为s个),要求满足【j-s+1,j】在【i,j-1】字符串区间出现。
这样要统计j开始的往前的情况,可以把字符串倒过来,把j作为第一个,然后1作为最后一个,求一遍next.然后更新sa[i][j] (i<j)
求出sa[i][j]后,就可以直接转移dp[i][j]=dp[i][j-1]+i-j+1-sa[i][j] //把第j个字符加上后,对整个子串个数的影响。减去在前面已经出现的。
代码:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define Maxn 2200
char sa1[Maxn];
char sa2[Maxn];
int next[Maxn];
int n,nn;
int sa[Maxn][Maxn],dp[Maxn][Maxn];
void getnext()
{
int j=0;
next[1]=0;
for(int i=2;i<=nn;i++)
{
while(j>0&&sa1[j+1]-sa1[i])
j=next[j];
if(sa1[j+1]==sa1[i])
j++;
next[i]=j;
}
return ;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%s",sa1+1);
n=strlen(sa1+1);
for(int i=1;i<=n;i++)
sa2[i]=sa1[n+1-i];
for(int i=1;i<=n;i++)
{
for(int j=i;j<=n;j++)
sa1[j-i+1]=sa2[j];
nn=n-i+1;
getnext();
sa[n+1-i-1][n+1-i]=next[2];
for(int j=i+2;j<=n;j++)
sa[n+1-i-(j-i)][n+1-i]=max(next[j-i+1],sa[n+1-i-(j-i)+1][n+1-i]);
}
for(int i=1;i<=n;i++)
{
dp[i][i]=1;
for(int j=i+1;j<=n;j++)
dp[i][j]=dp[i][j-1]+(j-i+1)-sa[i][j];
}
int q;
scanf("%d",&q);
while(q--)
{
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",dp[a][b]);
}
}
return 0;
}
原文:http://blog.csdn.net/cc_again/article/details/39963293