題目:
題解:
首先他給了我們關於next數組的提示,這挺好的。
先看到 num 的定義:不互相重疊的公共前後綴個數
說明 num不同於 next 記錄的是一個最大值,它記錄的是一個和值
那麼我們先用kmp數組用遞推求出ans[i]表示前綴後綴可以重疊的最長公共部分長度,這個可以用j蹦完之後直接ans[i]+1,加的1就是可以有個新的整體咯。
怎麼去除重疊的呢?一旦有一個遞歸了n層的next,比原前綴i的長度的一半要小,那麼這個next的遞推出的答案 ans 就是i的num了。這個可以感性理解
假如我們拿到的串是1e6個’a’,那麼上面那個算法就會被卡成 ,道理的話大家可以想一想(每一次遞歸都只會把next[i]變小1)
那麼我們需要做一個優化,來解決這個問題,而解決問題的核心就是:減少重複遞歸
就是如同求 next 時一樣的方法,我們不每次更新遞歸用的變量 j ,這樣,求完了 i 的答案以後, j 的位置一定在 的左邊,也就是它已經滿足要求了。每次j最多往右移動一位,即j++,這樣保證他一直在左邊。
這時再遞歸求解,總時間效率是 的
代碼:
#include <cstdio>
#include <cstring>
#define LL long long
using namespace std;
const int mod=1e9+7;
const int N=1000005;
int l,t[N],ans[N];char st[N];
void build()
{
t[0]=-1;
for (int i=0;i<l;i++)
{
int j=t[i];
while (j!=-1 && st[j]!=st[i]) j=t[j];
t[i+1]=++j; ans[i+1]=ans[j]+1;
}
}
int main()
{
int T;scanf("%d",&T);
while (T--)
{
scanf("%s",st);
l=strlen(st);ans[1]=1;
build();
LL cnt=1;int j=0;
for (int i=1;i<l;i++)
{
while (j!=-1 && st[j]!=st[i]) j=t[j];
j++;
while (j*2>i+1) j=t[j];
cnt=cnt*(LL)(ans[j]+1)%mod;
}
printf("%lld\n",cnt);
}
}