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