HDU 3336 Count the string:
题目:https://vjudge.net/problem/HDU-3336
分析:题目让我们求所有前缀在字符串中出现的次数,我们就假设dp[i] 为0 ~ i 这个前缀对答案的贡献,假如所有前缀在字符串中只出现一次,那么dp[i] = 1,但是现实情况肯定不是所有前缀只出现一次的,如果我们遍历到0~i 这个前缀,发现他有公共前后缀,这就说明前面的一个前缀被重复了,那么这个答案就dp[i] = 1(这个子缀本身)+1(与前缀重复的后缀) = 2吗?不一定,你怎么知道这个这个相同的前后缀没有公共前后缀?例如 a a c d a a 我们遍历到最后一个a,我们发现整个串aacdaa有最长公共前后缀为aa,说明前面的前缀aa在aacdaa中被重复了一次, 但同时最前面的a在这aacdaa中也被重复了一次,所以子串aacdaa对答案的贡献是1(它本身)+1(aa)+1(a) = 3,为什么没有倒数第二个a,他不是也被重复了吗,因为倒数第二个a在枚举到字串aacda的过程中已经被算过了。这样分析下来就有种递推的感觉,就是不断去跳next,跳一次答案加1
分析起来感觉不太简单,但写起来就只有一个式子,如果你看过其他题解也会发现:
[dp[i] = dp[nexts[i]]+1
]
其实给你只需要给你dp数组的含义这个式子就非常容易写出来,这里我称dp[i] 为0~i 这个子串的出现对答案的贡献,如果某个字串没有最长公共前后缀,那么dp[i] = 1,如果有,那他对答案的贡献就不只是他自己了,还多个后缀对答案的贡献,这个后缀在哪个位置呢?nexts[i] ,贡献为多少呢? dp[nexts[i]] ,所以这个式子就出来了
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
using namespace std;
const int MOD = 10007;
const int maxn = 200000+50;
int dp[maxn];
vector<int> nexts;
int T,N;
string str;
void build(string a){
int n = a.size();
nexts.resize(n+1);
nexts[0] = -1;
for(int i = 0,j = -1; i < n; nexts[++i] = ++j){
while(~j && a[i] != a[j]) j = nexts[j];
}
}
void solve()
{
cin>>N;
cin>>str;
for(int i = 0; i <= str.size(); i++) dp[i] = 0;
int ans = 0;
build(str);
for(int i = 1; i <= str.size(); i++){
dp[i] = (dp[nexts[i]] + 1) % MOD;
ans = (ans + dp[i]) % MOD;
}
cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>T;
for(int i = 1; i <= T; i++) solve();
return 0;
}