字符串(p)review學習筆記

目前會持續填充一些字符串的模板,後續會對有關題目進行進一步展開。

【Hash】

一個比較萬能的算法,在Θ(n)\Theta(n)的預處理之後,就可以對兩個字符串進行Θ()\Theta(常數)的比較了。

其基本思想是將一串字符串映射成一個數字,通過比較數字來比較字符串。

具體上是將每位字母映射成一個值,每加進來一個值,就把前面的值乘上一個位數。這些數用前綴和存起來,需要比較時移位相減就可以了。

舉個栗子,比如你現在有一個全是小寫字母的字符串,你可以將所有小寫字母一一對應:a->0,b->1,c->2,d->3…z->25。然後每次從後面加入一個新字母時就將原來的值乘上26就可以了。比如b=1,bc=126+2=28,bcd=12626+226+3=731b=1,bc=1*26+2=28,bcd=1*26*26+2*26+3=731
顯然這樣做數字很容易就會超過maxlongint,所以我們可以將得到的值對於一個大質數(使取模後的值儘可能均勻分佈)取模(其實不取模讓值自然溢出也可以不過很容易被卡)。可以證明當字符串長度不大時,出現相同值的概率是較低的
有時候我們會應對len<=1e6,query<=1e6的情況。爲進一步降低衝突的概率,我們可以多Hash,同時對多個大質數取模,這時要注意常數因子帶來的運行時間問題。
其實還有一種優化,我們可以取一個小質數基底,比如把原來的26進制換成29進制,然後取模,也可以使值均勻分佈,而且不容易被毒瘤出題人卡掉。實際應用中,我一般會直接開longlong不取模,好像也沒被卡過。

【模板】

給一個字符串,再有m個詢問l1,r1,l2,r2問 [l1,r2] 和 [l2,r2] 的字符串是否相同。
保證Len<=1e6,n<=1e6
【code】

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int base=19;
const int maxn=1e6+100;
int n,m;
char s[maxn];
unsigned long long H[maxn],mul[maxn];
inline void read(int &x){
    x=0;int fl=1;char tmp=getchar();
    while(tmp<'0'||tmp>'9'){if(tmp=='-')fl=-fl;tmp=getchar();}
    while(tmp>='0'&&tmp<='9')x=(x<<1)+(x<<3)+tmp-'0',tmp=getchar();
    x=x*fl;
}
inline int query(int l,int r){
    return H[r]-H[l-1]*mul[r-l+1];
}
int main(){
    scanf("%s",s+1);n=strlen(s+1);
    mul[0]=1;
    for(int i=1;i<=n;i++) H[i]=H[i-1]*base+s[i]-'a'+1,mul[i]=mul[i-1]*base;
    cin>>m;
    for(int i=1;i<=m;i++){
        static int l1,r1,l2,r2;
        read(l1),read(r1),read(l2),read(r2);
        if(query(l1,r1)==query(l2,r2))puts("Yes");
        else puts("No");
    }
    return 0;
}

【manacher算法】

Θ(n)\Theta(n)的時間求以每個點爲中心的最大回文串。(需要一定的想象力)

這是一個有對稱中心的算法,如果出現"abba"的字符串就會變得很棘手。所以我們要預先進行預處理,將每個字母之間和兩頭插入一個一定不會出現在字符串中的字符。可以預見,插入後要求字符串一定爲奇數長度,可以進行"馬拉車"。

manacher算法也是利用了字符串中一個常用的思想——利用已知信息減少重複運算
我們需要的有一個

經過插入字符處理的字符串,s[]。
記錄當點爲中心的最長迴文串的長度的數組,len[]。
記錄目前爲止最遠所到達的右邊界,mx。和mr的對稱中心,pos。

對於一個len[i],len[i]>=min(mx-i,len[pos2-i])是一定正確的。
pic-1
如上圖所示,如果由於在[pos
2-mx,mx]中的字符是對稱的,所以當len[j]的左端點大於mx的對稱點時,len[i]=len[j]。
反之,如果j的左端點超過或碰到mx的對稱點那麼len就至少爲mr-i且有可能向右擴展。

整個算法for循環i一遍。mx從0右移至n一遍,所以總複雜就是Θ(n)\Theta(n)

【模板】

給定一個字符串,求出其最長迴文子串。
多個測試數據,每個測試數據一行,僅由小寫字母組成的字符串。

【code】

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1.1e7+1000;
char s[maxn<<1];int n;
int len[maxn<<1];
int main(){
    int kase=0;
    while(1){
        scanf("%s",s+1);n=strlen(s+1);
        if(s[1]=='E'&&s[2]=='N'&&s[3]=='D')break;
        for(int i=n<<1;i>=2;i-=2)s[i]=s[i>>1],s[i-1]='*';s[0]='*';
        n=(n<<1)+1,s[n]='*',s[n+1]=0;
        int pos=0,mr=0,ans=0;
        for(int i=1;i<=n;i++){
            if(mr>1)len[i]=min(mr-i,len[(pos<<1)-i]);
            else len[i]=1;
            while(s[i-len[i]]==s[i+len[i]])len[i]++;
            if(i+len[i]>mr){
                mr=i+len[i];
                pos=i;
            }
            ans=max(ans,len[i]-1);
        }
        printf("Case %d: %d\n",++kase,ans);
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章