1. 程式人生 > 其它 >演算法 - 字串演算法 - 字串雜湊與經典例題《好文章》

演算法 - 字串演算法 - 字串雜湊與經典例題《好文章》

nodgd 寫了一篇文章,自認為這是一篇好文章。nodgd 的文章由 \(n\) 個小寫英文字母組成。文章的一個子串指的是文章中的一段連續的字母,子串的長度就是這一段的字母個數。nodgd 在文章中用了排比、對偶、前後照應之類的手法,所以就有很多個子串是相同或者相近的。為了向大家證明這是一篇好文章,nodgd 決定給自己的文章進行評分。nodgd 首先確定了一個整數 \(m\),然後統計出文章中有多少個不相同的長度為 \(m\) 的子串,這個數量就是文章的評分。然而,nodgd 懶得老老實實計算這個評分了,就把任務丟給了你。

輸入格式

第一行包含兩個整數 \(n,m\),表示文章的長度和需要統計的子串長度。

第二行包含一個長度為 \(n\) 的只包含小寫字母的字串。

輸出格式

輸出一行一個整數,表示文章的評分。

輸入輸出樣例

樣例 1

輸入

5 3
aaaab

輸出

2

長度為 \(3\) 的子串有 \(3\) 個,分別是 aaa、aaa、aab,其中不同的只有 \(2\) 個。

樣例 2

輸入

9 3
abcabacba

輸出

7

共有7個長度為3的子串,每個長度為3的子串都不同。

資料範圍

\(30\%\) 的資料,\(1 ≤ m ≤ n ≤ 200\)

\(50\%\) 的資料,\(1 ≤ m ≤ n ≤ 2000\)

\(70\%\) 的資料,\(1 ≤ m ≤ 50 ≤ n ≤ 200000\)

\(100\%\) 的資料,\(1 ≤ m ≤ n ≤ 200000\)

題解

使用雙雜湊,這裡我們開兩個hash表,然後給同一個字串附上兩個hash值,在判斷兩個字串是否相同時,必須兩個hash值都相同才行。

#include<cmath>
#include<cstdio>
const int maxm=200010;
long long hash_a[maxm],hash_b[maxm];
long long Hash_a[maxm],Hash_b[maxm];
long long Hash_pow_a[maxm],Hash_pow_b[maxm];//指數陣列
int n,m,ans;
char str[maxm];
void Init(int p1,int P1,int p2,int P2)
{
    for(int i=1;i<=n;i++)
    {
        Hash_a[i]=(1ll*Hash_a[i-1]*p1+(str[i]-'a'+1))%P1;
        Hash_pow_a[i]=1ll*Hash_pow_a[i-1]*p1%P1;
        Hash_b[i]=(1ll*Hash_b[i-1]*p2+(str[i]-'a'+1))%P2;
        Hash_pow_b[i]=1ll*Hash_pow_b[i-1]*p2%P2;
    }
}
int hash_(int l,int r,int p,int P,int k)
{
    if(k==1) return (Hash_a[r]-1ll*Hash_a[l-1]*Hash_pow_a[r-l+1]%P+P)%P;
    return (Hash_b[r]-1ll*Hash_b[l-1]*Hash_pow_b[r-l+1]%P+P)%P;
}
void _qst(int l,int r)//快排,可以使用sort,開結構體
                     //根據HashA的值排序。
{
    int i,j,m,mm,t;
    i=l;j=r;
    m=hash_a[(i+j)>>1];//位運算,相當於除以2
    mm=hash_b[(i+j)>>1];
    while(i<=j)
    {
        while(hash_a[i]<m||(hash_a[i]==m&&hash_b[i]<mm))i++;
        while(hash_a[j]>m||(hash_a[j]==m&&hash_b[j]>mm))j--;
        if(i<=j)
        {
            t=hash_a[i];hash_a[i]=hash_a[j];hash_a[j]=t;
            t=hash_b[i];hash_b[i]=hash_b[j];hash_b[j]=t;
            i++;j--;
        }
    }
    if(i<r)_qst(i,r);
    if(l<j)_qst(l,j);
}
int main()
{
    scanf("%d%d",&n,&m);
    scanf("%s",str+1);
    Hash_pow_a[0]=1;
    Hash_pow_b[0]=1;
    Init(61,1000000007,97,1000000009);
    for(int i=1;i<=n-m+1;i++)
    {
        hash_a[i]=hash_(i,i+m-1,61,1000000007,1);
        hash_b[i]=hash_(i,i+m-1,97,1000000009,2);
    }
    _qst(1,n-m+1);
    for(int i=1;i<=n-m+1;i++)
    {
        if(hash_a[i]==hash_a[i-1]&&hash_b[i]==hash_b[i-1])
            continue;
        ans++;
    }
    printf("%d",ans);
}