[jzoj 4876] 基因突變

Description

邪惡的707剛剛從白堊紀穿越回來,心中產生了一個念頭:我要統治人類!
但是統治人類是很龐大且複雜的一個工程,707嘗試了洗腦,催眠,以及武裝鎮壓都沒能成功地統治人類,於是她決定從科學上對人類的基因進行研究從而達到他的目的。
707獲取了人類的基因信息並嘗試對基因進行實驗。他發現可以把人類的基因看做一個只包含小寫字母的字符串,並定義從頭開始任意長度的基因爲“源頭基因”人類身上與源頭基因完全匹配的片段越多,這個人就越容易被控制。於是707就開始了他邪惡的計劃……
作爲人類衛士的射手ZMiG自然不會讓707得逞,他決定拯救人類,現在他拿到了其中一個人被改造後的基因,他想請你統計一下它的基因中究竟有多少基因片段是可以與源頭基因相匹配的

Input

輸入一個只包含小寫字母的字符串S

Output

輸出一個整數,代表可以與源頭基因相匹配的基因片段數量。

Sample Input

【樣例輸入1】
aaba

【樣例輸入2】
niconiconi

Sample Output

【樣例輸出1】
6

【樣例解釋1】
這六個片段分別爲(1,1),(1,2),(1,3),(1,4),(2,2),(4,4)

【樣例輸出2】
18

Data Constraint

對於30% 的數據,|S|<= 200
對於60% 的數據,|S|<= 2000
對於100%的數據,|S|<= 10^6

The Solution

這道題乍一看,怎麼一眼kmp??
沒錯這就是kmp,而且還是類似的exkmp!!
比賽時還有人打SA!!不過可憐的被卡常只有80分了。
SA的常數太大了
真的是無奇不有。。。
而我比賽是則打得是二分+hash求最長公共前綴
時間複雜度也是O(n log n)的。
有人說“SA 也是n log n”的爲什麼沒過~~
你也不看看常數多大!!

還有另一種題解,就是kmp的:

相當於對於每個前綴求有多少個字符串和他相等
可以先求出 KMP 的 next 數組,然後對於一個位置,
每跳一次 next 就說明多了一個合法的匹配。
一個暴力方法是直接從每個節點開始跳統計答案。
顯然我們可以用 f[i]表示以 i 爲結尾有多少組這樣的匹配,
然後可以發現 f[i]=f[next[i]]+1,這樣 O(N)掃一遍就可以了

CODE

墊底程序。。。O(n log n)

#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define N 1000005

using namespace std;

typedef long long ll;

const int mo = 998244353;

char s[N];
ll ans = 0,Pret[N],h[N];

int Calc(int x,int y)
{
    return (h[x+y-1] - (ll)h[x-1] * Pret[y] % mo + mo) % mo;
}

ll find(int l,int r)
{
    int i = l;
    ll res = 0;
    while (l <= r)
    {
        int mid = (l + r) >> 1;
        if (Calc(i,mid - i + 1) == Calc(1,mid - i + 1)) res = mid - i + 1, l = mid + 1;
        else r = mid - 1;
    }
    return res;
}

int main()
{
    freopen("gene.in","r",stdin);
    freopen("gene.out","w",stdout);
    scanf("%s",s + 1);
    int n = strlen(s + 1);
    Pret[0] = 1;
    fo(i,1,n)
    {
        Pret[i] = (ll)Pret[i - 1] * 26 % mo;
        h[i] = ((ll)h[i - 1] * 26 % mo + s[i] - 'a') % mo;
    }
    fo(i,1,n) ans += find(i,n);
    printf("%lld\n",ans);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章