[BZOJ3670][NOI2014]動物園(KMP)

題目:

我是超鏈接

題解:

首先他給了我們關於next數組的提示,這挺好的。
先看到 num 的定義:不互相重疊的公共前後綴個數
說明 num不同於 next 記錄的是一個最大值,它記錄的是一個和值

那麼我們先用kmp數組用遞推求出ans[i]表示前綴後綴可以重疊的最長公共部分長度,這個可以用j蹦完之後直接ans[i]+1,加的1就是可以有個新的整體咯。

怎麼去除重疊的呢?一旦有一個遞歸了n層的next,比原前綴i的長度的一半要小,那麼這個next的遞推出的答案 ans 就是i的num了。這個可以感性理解

假如我們拿到的串是1e6個’a’,那麼上面那個算法就會被卡成 O(n2) ,道理的話大家可以想一想(每一次遞歸都只會把next[i]變小1)

那麼我們需要做一個優化,來解決這個問題,而解決問題的核心就是:減少重複遞歸

就是如同求 next 時一樣的方法,我們不每次更新遞歸用的變量 j ,這樣,求完了 i 的答案以後, j 的位置一定在i2 的左邊,也就是它已經滿足要求了。每次j最多往右移動一位,即j++,這樣保證他一直在左邊。

這時再遞歸求解,總時間效率是 O(n)

代碼:

#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);
    }
}
發佈了722 篇原創文章 · 獲贊 547 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章