字符串专题

字符串

前言

假期的时候刷了点字符串的专题,感觉板子变得更加普适和完善了

KMP

kmp算法实际上就是找最长公共前后缀,之前一直都是用的acwing的板子,根据董晓算法的板子和自己写题的习惯完善了一下。

kmp还可以用来找循环节

板子

bool KMP(string s, string t){
    int n = s.size(), m = t.size();
    vector<int>ne(n+1);
    s = "?" + s;
    t = "?" + t;
    for(int i = 2, j = 0;i <= m;++i){
        while(j && t[i] != t[j+1]) j = ne[j];
        if(t[i] == t[j+1]) j++;
        ne[i] = j;
    }
    for(int i = 1, j = 0;i <= n;++i){
        while(j && s[i] != t[j+1]) j = ne[j];
        if(s[i] == t[j+1]) j++;
        if(j == m){
            j = ne[j];
            return true;
        }
    }
    return false;
}

EXKMP

exkmp可以用来某个串的任意后缀的最长公共前缀的长度,称为z函数,如果是两个不同的串就叫p函数。

实际上我觉得exkmp和manacher在代码上比较像。

板子

/*
    求字符串s的任意后缀的最长公共前缀(LCP)的长度

    利用逆串和原串做p函数还可以得到一个串的后缀/前缀是否为回文串
 */

/*
    求s串的LCP
 */
vector<int> get_z(string s){
    int n = s.size();
    s = "?"+s;
    vector<int>z(n+1);
    z[1] = n;
    for(int i = 2, l, r = 0;i <= n;++i){
        if(i <= r) z[i] = min(z[i-l+1], r-i+1);
        while(i+z[i]<=n && s[1+z[i]]==s[i+z[i]]) z[i]++;
        if(i+z[i]-1>r) l = i, r = i+z[i]-1;
    }
    return z;
}
/*
    求s串(作为前缀)和t串(作为后缀)的LCP
 */
vector<int> get_p(vector<int>z, string s, string t){
    int n = s.size(), m = t.size();
    s = "?"+s;
    t = "?"+t;
    vector<int>p(m+1);
    for(int i = 1, l, r = 0;i <= m;++i){
        if(i <= r) p[i] = min(z[i-l+1], r-i+1);
        while(1+p[i]<=n && i+p[i]<=m && s[1+p[i]]==t[i+p[i]]) p[i]++;
        if(i+p[i]-1 > r) l = i, r = i+p[i]-1;
    }
    return p;
}

manacher

线性时间内找到每个点的最长回文半径,很多回文串的题整点manacher就能做

板子

string preProcess(string a){
    int n = a.size();
    string s = "?#";
    for(int i = 0;i < n;++i){
        s += a[i];
        s += "#";
    }
    return s;
}
vector<int> get_d(string s, int n){
    vector<int>d(n+1);
    d[1] = 1;
    for(int i = 2, l, r = 1;i <= n;++i){
        if(i <= r) d[i] = min(d[r-i+l], r-i+1);
        else d[i] = 1;
        while(s[i-d[i]]==s[i+d[i]]) d[i]++;
        if(i+d[i]-1 > r) l = i-d[i]+1, r = i+d[i]-1;
    }
    return d;
}
void solve(){
    string s;cin >> s;
    string str = preProcess(s);
    int n = str.size()-1;
    vector<int>d = get_d(str, n);
    cout << *max_element(all(d))-1 << endl;
    /*
        得到原串的L R区间(从1开始计数)
     */
    // for(int i = 1;i <= n;++i){
    //     if(d[i]-1 > r-l+1){
    //         l = i/2-(d[i]-1)/2;
    //         r = i/2+(d[i]-1)/2;
    //         if(i%2){
    //             l++;
    //         }
    //     }
    // }
}
posted @ 2023-03-08 19:46  _77  阅读(89)  评论(0编辑  收藏  举报