传送门:洛谷P2375
一直到写到这道题目才发现我一直都理解了假的KMP……fail数组:fail[i]为从1-i(包含i)在内的字符串,相同的最长前后缀长度。
那么我们可以先思考暴力:先求出所有的fail,再不断往上跳,那么跳到的节点中(fail<<1)<i的个数即为num值。但这样的复杂度太高了,所以我们要进一步优化。
可以发现每一个节点所指向的fail节点是唯一的,但一个点可能是多个节点的fail,这是一个树形的关系。且在这个树形关系上,越靠近根节点的fail值也就越小。所以我们逐层标记Num值,直到找到符合条件的节点,则这个节点所标记的值就是它&它上方所有节点的个数。(若该点满足条件,则在它之上的一定满足)。
#include <bits/stdc++.h> using namespace std; #define maxn 10000 #define ll long long #define p 1000000007 int fail[maxn], num[maxn]; char s[maxn]; ll solve() { ll ans = 1; int len = strlen(s + 1); memset(fail, 0, sizeof(fail)); memset(num, 0, sizeof(num)); num[1] = 1; for(int i = 2, j = 0; i <= len; i ++) { while(j && s[i] != s[j + 1]) j = fail[j]; if(s[i] == s[j + 1]) j ++; fail[i] = j; num[i] = num[j] + 1; } for(int i = 2, j = 0; i <= len; i ++) { while(j && s[i] != s[j + 1]) j = fail[j]; if(s[i] == s[j + 1]) j ++; while((j << 1) > i) j = fail[j]; ans = (num[j] + 1) * ans; ans %= p; } return ans; } int main() { int T; scanf("%d", &T); while(T --) { scanf("%s", s + 1); printf("%lld\n", solve()); } return 0; }