hihocoder 1987 字符串hash+dp
链接http://hihocoder.com/problemset/problem/1987
题意:
输入一个长度为n的字符串,问有多少种划分方式使得划分后的每个字符串都是循环串。循环串定义为:对于一个字符串A,如果存在不等于A的字符串B使得连续若干个B连接起来后可以组成A,那么称A为循环串。
例如AABAAB就是循环串,而AABAAC则不是。
答案可能很大,输出对1e9+7取模。
思路:
暴力hash dp一下,枚举子串,然后往后比较,复杂度很好分析,一个"循环串"最多被枚举它的因数的次数,显然是sqrt(n)的,那么知道了所有的"循环串"就可以dp了,就是首尾相接的可以转移。dp[R]=(dp[R]+dp[L-1])
,同时注意一个“循环串”只统计一次。
代码:
#include <bits/stdc++.h>
#define LL long long
#define pii pair<int,int>
#define PB push_back
#define X first
#define Y second
using namespace std;
const int maxn = 1005,seed=131,mod = 1e9+7;
LL p[maxn],s[maxn];
string str;
LL add(LL a,LL b){
return (a+b)%mod;
}
LL mul(LL a,LL b){
return (a*b)%mod;
}
LL sub(LL a,LL b){
return (a-b+mod)%mod;
}
LL get_hash(int l ,int r){
l++,r++;
return sub(s[r],mul(s[l-1],p[r-l+1]));
}
LL dp[maxn],vis[maxn];
void find_pair(int n){
for(int i=0;i<n;i++){
for(int j=i;j<n;j++) vis[j]=0;
for(int j=i;j<n;j++){
int sk=j-i+1;
for(int k=j+1;k+sk-1<n;k+=sk){
if(get_hash(i,j)==get_hash(k,k+sk-1)){
if(vis[k+sk-1]==0)
dp[k+sk-1]=add(dp[k+sk-1],i==0?1:dp[i-1]),vis[k+sk-1]=1;
}
else break;
}
}
}
}
int main(){
int t,n;
cin>>t;
while(t--){
cin>>n>>str;
p[0]=1;s[0]=0;
for(int i=0;i<=n;i++) dp[i]=0;
for(int i=1;i<=n;i++)p[i]=mul(p[i-1],seed);
for(int i=1;i<=n;i++)s[i]=add(mul(s[i-1],seed),str[i-1]);
find_pair(n);
cout<<dp[n-1]<<endl;
}
return 0;
}