zz:https://www.cnblogs.com/GXZlegend/p/6855581.html
Sol:
先求出next,然后我们可以发现如果x和next[x](x>0)连一条边,那么就是一颗树,而所求的num是每个点的所有祖先节点中最后一个长度小于等于len[x]的点之前的祖先节点个数-1(0不为答案),也即祖先节点中最后一个长度小于等于len[x]的点的深度(deep[0]=0)。于是我们可以维护一个栈,并在其中二分查找得到答案。
#include <bits/stdc++.h> #include <cstring> #include <algorithm> #define N 1000010 using namespace std; int fail[N] , num[N] , head[N] , to[N] , next[N] , cnt , sta[N] , top; char str[N]; void add(int x , int y) { to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt; } void dfs(int x) { int i; sta[++top] = x; //将编号为x的号进栈 for(i = head[x] ; i ; i = next[i]) { num[to[i]] = upper_bound(sta + 1 , sta + top + 1 , to[i] >> 1) - sta - 1 ; //求出to[i]这个点,所在的栈中,有多少值是小于它的一半的。 //upper_bound是找第一个大于某个元素的位置,也就是找有多少个数是小于它的. dfs(to[i]); } sta[top -- ] = x; } int main() { int T; scanf("%d" , &T); while(T -- ) { int n , i = 0 , j = -1; long long ans = 1; memset(head , 0 , sizeof(head)) , cnt = 1; scanf("%s" , str) , n = strlen(str); fail[0] = -1; while(i < n) { while(~j && str[j] != str[i]) j = fail[j]; fail[++i] = ++j; } // for (int i=1;i<=n;i++) // cout<<i<<" "<<fail[i]<<endl; for(i = 1 ; i <= n ; i ++ ) //连一条边从fail[i]到i add(fail[i] , i); dfs(0);//从0号点开始,将其入栈,这样每个num[i]的值于少为1 for(i = 1 ; i <= n ; i ++ ) ans = ans * num[i] % 1000000007; printf("%lld\n" , ans); } return 0; }
原文:https://www.cnblogs.com/cutemush/p/12380457.html