1. 程式人生 > 實用技巧 >字串學習筆記

字串學習筆記

雜湊

  • 雜湊理解起來就相當於用一種對映方式,以實現快速對比字串或者其他東西(比如圖)的功能,有時候利用技巧還能實現字典序大小的比較或者反對映操作。
  • 其思想是非常重要而且容易理解的,關鍵在於hash方法以及程式設計上的技巧。

實現方法

  • 字首和是hash的常見用法

方法一

#include <bits/stdc++.h>
using namespace std;
const int maxn=4e5+100;
const int base=233;
char s[maxn];
int poww[maxn],hash[maxn];
int main(){
    while(cin>>s+1){
        int len=strlen(s+1);
        poww[1]=233;
        for(int i=2;i<=len;i++)
            poww[i]=poww[i-1]*base;
        hash[1]=s[1];
        for(int i=2;i<=len;i++)
            hash[i]=hash[i-1]*base+s[i];
    }
}

方法二

  • 把字串變成數字
#define ull unsigned long long
ull gethash(string s){
    ull tmp=0;
    for(int i=0;i<s.size();i++)
        tmp=tmp*233+(s[i]-'a');
    return tmp;
}

一道例題

題目

已知字串A與B,有n組詢問,每次詢問給l,r,s,t,要求O(1)判斷A[l…r]與B[s…t]是否相同。

題解

  • 既然已經求得兩個字串的hash陣列,就直接取A[l..r]的雜湊值與B[s…t]的雜湊值進行比較即可。
  • 那麼如何更加方便的比較呢?
  • 我們使用 \(hash[i]=hash[i-1]*10+s[i]\) 的方法進行雜湊得雜湊陣列\(hasha,hashb\)
  • 然後我們發現一個問題:
    • 舉一例子:

    \(s1: 1 2 3 4 5 1\)
    \(s2: 2 3 4 1 1 2\)

    • \(s1[2…4]\)\(s2[1…3]\) 是相同的,但我們發現,\(hasha[4]-hasha[1]\)\(hashb[3]-hashb[0]\)並不相同,為什麼呢?
    • 回憶我們的雜湊過程,發現我們要乘上一個 \(poww[i]\),這樣我們求出來的才是真正的雜湊,即\(hash[r]-hash[l-1]*pow[i]\)

code

while(n--){
    cin>>l>>r>>s>>t;
    len=r-l+1;
    if(hasha[r]-hasha[l-1]*poww[len] == hashb[s]-hahsb[t-1]*poww[len])
        puts("YES");
    else puts("NO");
}

再來幾道例題

manachar

普及知識

  • 迴文串:abcba(√),abba(√),abda(×)(就是正著看和倒著看相同的串)
  • 子串≠子序列(子串是連續的,子序列可不連續)

一道例題

求一個字串中最長迴文子串長度。
例:字串:abbababa   最長迴文子串:ababa

題解

  • manacher是一種暴力的優化,可以用O(n)的時間解決上述問題,但是我真的沒怎麼見過這玩意派上用場
  • 可以學一下回文自動機,那個的作用要比這個大得多...
  • 因為偶迴文串比較難處理(不是以某個字元為中心),所以首先我們要通過在每個相鄰字元中間插入一個相同的字元(比如’|’,一定要是沒有出現過的),這樣預處理操作一發後我們會發現所有的迴文串都是奇迴文串,並且它也不會影響答案。
  • 把預處理後的字串叫做S
  • 最後的出來的答案就是 S的最長迴文子串長+1
  • 複雜度O(n)也是容易理解的,因為R只有一次從1到n,以及i只有一次從1到n,而且二者是並列關係。
  • 此種演算法心中有印象即可,十分不常考。

code

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e8+100;
char a[maxn<<1];
int f[maxn<<1];
int main(){
    int cnt=0;a[0]='~';a[++cnt]='|'; char ch; int ans=0;
    while((ch=getchar())>='a' && ch<='z') a[++cnt]=ch,a[++cnt]='|';
    for(int i=1,r=0,mid=0;i<=cnt;i++){ //mid中心,i現在的點,f[]半徑 
        if(i <= r) f[i]=min(f[mid*2-i],r-i+1);
        while(a[i-f[i]]==a[i+f[i]]) f[i]++;
        if(f[i]+i>r) r=f[i]+i-1,mid=i;
        if(f[i]>ans) ans=f[i];
    }
    printf("%d\n",ans-1);
    return 0;
}

推薦例題

有興趣可以做一做洛谷板子題