HDU 3336 Count the string

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;
}
"没有天赋异禀,那就加倍努力"
原文地址:https://www.cnblogs.com/Beic233/p/13047971.html