[kuangbin带你飞]专题十六 KMP & 扩展KMP & Manacher

A
KMP模版

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int maxn = 1000000 + 5;
const int maxm = 10000 + 5;

void getFail(int* p, int m, int* f) {
    f[0] = 0, f[1] = 0;
    for(int i = 1; i < m; ++i) {
        int j = f[i];
        while(j && p[i] != p[j]) j = f[j];
        f[i + 1] = p[i] == p[j] ? j + 1 : 0;
    }
}
int find(int* t, int n, int* p, int m, int* f) {
    getFail(p, m, f);
    int j = 0;
    for(int i = 0; i < n; ++i) {
        while(j && p[j] != t[i]) j = f[j];
        if(p[j] == t[i]) j++;
        if(j == m) return i - m + 1;
    }
    return -1;
}
int a[maxn], b[maxm];
int f[maxn];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    int t;
    cin >> t;
    while(t--) {
        int n, m;
        cin >> n >> m;
        for(int i = 0; i < n; ++i) cin >> a[i];
        for(int i = 0; i < m; ++i) cin >> b[i];
        int ans = find(a, n, b, m, f);
        cout << (ans == -1 ? -1 : ans + 1) << endl;
    }
}

B
kmp魔改题,kmp匹配成功后变为失配的位置,即最长公共前后缀

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;
const int maxm = 1e4 + 5;
void getFail(char* p, int* f) {
    int m = strlen(p);
    f[0] = 0, f[1] = 0;
    for(int i = 1; i < m; ++i) {
        int j = f[i];
        while(j && p[i] != p[j]) j = f[j];
        f[i + 1] = p[i] == p[j] ? j + 1 : 0;
    }
}
int find(char* t, char* p, int* f) {
    int n = strlen(t), m = strlen(p);
    getFail(p, f);
    int j = 0;
    int ans = 0;
    for(int i = 0; i < n; ++i) {
        while(j && p[j] != t[i]) j = f[j];
        if(p[j] == t[i]) j++;
        if(j == m) {
            ans++;
            j = f[j];
        }
    }
    return ans;
}
char P[maxm], T[maxn];
int f[maxm];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin >> t;
    while(t--) {
        cin >> P >> T;
        cout << find(T, P, f) << endl;
    }
}

C

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;
const int maxm = 1e4 + 5;
void getFail(char* p, int* f) {
    int m = strlen(p);
    f[0] = 0, f[1] = 0;
    for(int i = 1; i < m; ++i) {
        int j = f[i];
        while(j && p[i] != p[j]) j = f[j];
        f[i + 1] = p[i] == p[j] ? j + 1 : 0;
    }
}
int find(char* t, char* p, int* f) {
    int n = strlen(t), m = strlen(p);
    getFail(p, f);
    int j = 0;
    int ans = 0;
    for(int i = 0; i < n; ++i) {
        while(j && p[j] != t[i]) j = f[j];
        if(p[j] == t[i]) j++;
        if(j == m) {
            ans++;
            j = 0;
        }
    }
    return ans;
}
char P[maxm], T[maxn];
int f[maxm];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    while(cin >> T) {
        if(strlen(T) == 1 && T[0] == '#') break;
        cin >> P;
        cout << find(T, P, f) << endl;
    }
}

D
补全循环节
f[n] && n * (n - f[n]) == 0表示存在n / (n - f[n])个循环节
若不存在,则n-f[n]为存在的最小循环节长度,我们即需要补全n - f[n] - n % (n - f[n])

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;
void getFail(char* p, int* f) {
    int m = strlen(p);
    f[0] = 0, f[1] = 0;
    for(int i = 1; i < m; ++i) {
        int j = f[i];
        while(j && p[i] != p[j]) j = f[j];
        f[i + 1] = p[i] == p[j] ? j + 1 : 0;
    }
}
char P[maxn];
int f[maxn];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin >> t;
    while(t--) {
        cin >> P;
        getFail(P, f);
        int n = strlen(P);
        if(f[n] == 0) cout << n << endl;
        else if(n % (n - f[n]) == 0) cout << 0 << endl;
        else cout << (n - f[n] - n % (n - f[n])) << endl;
    }
}

E
循环节

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;
void getFail(char* p, int* f) {
    int m = strlen(p);
    f[0] = 0, f[1] = 0;
    for(int i = 1; i < m; ++i) {
        int j = f[i];
        while(j && p[i] != p[j]) j = f[j];
        f[i + 1] = p[i] == p[j] ? j + 1 : 0;
    }
}
char P[maxn];
int f[maxn];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    int n, cas = 1;
    while(cin >> n) {
        if(n == 0) break;
        cin >> P;
        getFail(P, f);
        cout << "Test case #" << cas++ << endl;
        for(int i = 0; i <= n; ++i) {
            if(f[i] && i % (i - f[i]) == 0) {
                cout << i << " " << i / (i - f[i]) << endl;
            }
        }
        cout << endl;
    }
}

G

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;
void getFail(char* p, int* f) {
    int m = strlen(p);
    f[0] = 0, f[1] = 0;
    for(int i = 1; i < m; ++i) {
        int j = f[i];
        while(j && p[i] != p[j]) j = f[j];
        f[i + 1] = p[i] == p[j] ? j + 1 : 0;
    }
}
char P[maxn];
int f[maxn];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    int n, cas = 1;
    while(cin >> P) {
        if(strlen(P) == 1 && P[0] == '.') break;
        getFail(P, f);
        int n = strlen(P);
        if(f[n] && n % (n - f[n]) == 0) cout << n / (n - f[n]) << endl;
        else cout << 1 << endl;
    }
}

H
next数组的意义

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;
void getFail(char* p, int* f) {
    int m = strlen(p);
    f[0] = 0, f[1] = 0;
    for(int i = 1; i < m; ++i) {
        int j = f[i];
        while(j && p[i] != p[j]) j = f[j];
        f[i + 1] = p[i] == p[j] ? j + 1 : 0;
    }
}
char P[maxn];
int f[maxn];
int ans[maxn], tot;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    while(cin >> P) {
        getFail(P, f);
        int n = strlen(P);
        tot = 0;
        ans[tot++] = n;
        while(f[n]) {
            ans[tot++] = f[n];
            n = f[n];
        }
        for(int i = tot - 1; i >= 0; --i) {
            if(i == tot - 1) cout << ans[i];
            else cout << " " << ans[i];
        }
        cout << endl;
    }
}

I
暴力枚举最长公共子串,用kmp判断即可

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int maxn = 60 + 5;
void getFail(char* p, int m, int* f) {
    f[0] = 0, f[1] = 0;
    for(int i = 1; i < m; ++i) {
        int j = f[i];
        while(j && p[i] != p[j]) j = f[j];
        f[i + 1] = p[i] == p[j] ? j + 1 : 0;
    }
}
bool find(char* t, char* p, int m, int* f) {
    int n = strlen(t);
    getFail(p, m, f);
    int j = 0;
    int ans = 0;
    for(int i = 0; i < n; ++i) {
        while(j && p[j] != t[i]) j = f[j];
        if(p[j] == t[i]) j++;
        if(j == m) return true;
    }
    return false;
}
char T[15][maxn];
char ch[4] = {'A', 'C', 'G', 'T'};
char P[maxn];
int f[maxn];
int ans = 0, n;
char ans_ch[maxn];
void dfs(int m) {
    for(int i = 0; i < 4; ++i) {
        P[m] = ch[i];
        bool flg = true;
        for(int j = 0; j < n; ++j) {
            if(!find(T[j], P, m + 1, f)) {
                flg = false;
                break;
            }
        }
        if(flg) {
            if(ans < m + 1) {
                for(int j = 0; j < m + 1; ++j) ans_ch[j] = P[j];
                ans = m + 1;
            }
            dfs(m + 1);
        }
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int t;
    cin >> t;
    while(t--) {
        ans = 0;
        cin >> n;
        for(int i = 0; i < n; ++i) cin >> T[i];
        dfs(0);
        if(ans < 3) cout << "no significant commonalities" << endl;
        else {
            for(int i = 0; i < ans; ++i) 
                cout << ans_ch[i];
            cout << endl;
        }
    }
}

J
拼接两个字符串,将两个字符串中间加入特殊字符.

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;
void getFail(char* p, int* f) {
    int m = strlen(p);
    f[0] = 0, f[1] = 0;
    for(int i = 1; i < m; ++i) {
        int j = f[i];
        while(j && p[i] != p[j]) j = f[j];
        f[i + 1] = p[i] == p[j] ? j + 1 : 0;
    }
}
char P[maxn], T[maxn];
int f[maxn];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    while(cin >> P) {
        int n = strlen(P);
        cin >> T;
        int m = n + strlen(T) + 1;
        for(int i = n + 1; i < m; ++i) P[i] = T[i - n - 1];
        P[n] = '.';
        getFail(P, f);
        if(f[m] == 0) cout << 0 << endl;
        else {
            for(int i = 0; i < f[m]; ++i) cout << P[i];
            cout << " " << f[m] << endl;
        }
    }
}

K
next数组计算的是子串中最长公共前后缀的长度
我们令cnt[i]表示以第i个字符结尾的所有前缀出现的次数,则cnt[i]=cnt[next[i]]+1

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int maxn = 200000 + 5;
const int maxm = 200000 + 5;
const int mod = 10007;
void getFail(char* p, int* f) {
    int m = strlen(p);
    f[0] = 0, f[1] = 0;
    for(int i = 1; i < m; ++i) {
        int j = f[i];
        while(j && p[i] != p[j]) j = f[j];
        f[i + 1] = p[i] == p[j] ? j + 1 : 0;
    }
}
int find(char* t, char* p, int* f) {
    int n = strlen(t), m = strlen(p);
    getFail(p, f);
    int j = 0;
    int ans = 0;
    for(int i = 0; i < n; ++i) {
        while(j && p[j] != t[i]) j = f[j];
        if(p[j] == t[i]) j++;
        if(j == m) {
            ans++;
            j = f[j];
        }
    }
    return ans % mod;
}
char P[maxm], T[maxn];
int f[maxm], cnt[maxn];
int main()
{
    int t;
    scanf("%d", &t);
    while(t--) {
        int n;
        scanf("%d", &n);
        scanf("%s", T);
        getFail(T, f);
        int ans = 0;
        memset(cnt, 0, sizeof(cnt));
        for(int i = 1; i <= n; ++i) {
            cnt[i] = cnt[f[i]] + 1;
            ans = (ans + cnt[i]) % mod;
        }
        printf("%d\n", ans);
    }
}

L
原本为密文 + 明文,我们通过给出转换为-》明文 + 未知
即使用exkmp找最长前后缀

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 5;

void GetNext(char *str, int* Next)
{
    int i=0,j,pos,len=strlen(str);
    Next[0]=len;   //初始化next[0]
    while(i+1<len&&str[i]==str[i+1]) i++; //初始化next[1]
    Next[1]=i;
    pos=1; //初始化pos
    for(i=2; i<len; i++)
    {
        if(i+Next[i-pos]<pos+Next[pos])
            Next[i]=Next[i-pos];
        else
        {
            j=pos+Next[pos]-i;
            if(j<0) j=0;    //如果i>pos+next[pos],则从头开始
            while(i+j<len&&str[i+j]==str[j]) j++;
            Next[i]=j;
            pos=i;
        }
    }
}
void ExKMP(char *s1,char *s2, int *Next, int *extend)         //s2的后缀和s1的最长前缀
{
    int i=0,j,pos,len1=strlen(s1),len2=strlen(s2);
    GetNext(s2, Next);
    while(i<len1&&i<len2&&s1[i]==s2[i]) i++;
    extend[0]=i;
    pos=0;
    for(int i=1; i<len1; i++)
    {
        if(i+Next[i-pos]<pos+extend[pos])
            extend[i]=Next[i-pos];
        else
        {
            j=pos+extend[pos]-i;
            if(j<0) j=0;
            while(i+j<len1&&j<len2&&s1[i+j]==s2[j]) j++;
            extend[i]=j;
            pos=i;
        }
    }
}
char a[maxn], T[maxn], P[maxn], match[200];
int f[maxn], ex[maxn];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    int t;
    cin >> t;
    while(t--) {
        cin >> a;
        for(int i = 0; a[i] != '\0'; ++i) match[a[i]] = i + 'a';
        cin >> T;
        for(int i = 0; T[i] != '\0'; ++i) P[i] = match[T[i]];
        int n = strlen(T);
        int maxx = n;
        ExKMP(T, P, f, ex);
        for(int i = 0; i < n; ++i) {
            if(i + ex[i] >= n && i >= ex[i]) {
                maxx = i;
                break;
            }
        }
        for(int i = 0; i < maxx; ++i) cout << T[i];
        for(int i = 0; i < maxx; ++i) cout << match[T[i]];
        cout << endl;
    }
}

M
暴力枚举子串,KMP匹配即可

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int maxn = 100 + 5;
void getFail(char* p, int m, int* f) {
    f[0] = 0, f[1] = 0;
    for(int i = 1; i < m; ++i) {
        int j = f[i];
        while(j && p[i] != p[j]) j = f[j];
        f[i + 1] = p[i] == p[j] ? j + 1 : 0;
    }
}
bool find(char* t, char* p, int m, int* f) {
    int n = strlen(t);
    getFail(p, m, f);
    int j = 0;
    int ans = 0;
    for(int i = 0; i < n; ++i) {
        while(j && p[j] != t[i]) j = f[j];
        if(p[j] == t[i]) j++;
        if(j == m) return true;
    }
    return false;
}
char P1[maxn], P2[maxn], T[maxn][maxn];
int f1[maxn], f2[maxn];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    int t;
    cin >> t;
    while(t--) {
        int n;
        cin >> n;
        for(int i = 0; i < n; ++i) 
            cin >> T[i];
        int m = strlen(T[0]), tot;
        int ans = 0;
        for(int i = 0; i < m; ++i) {
            for(int j = i + ans; j < m; ++j) {
                tot = 0;
                for(int k = i; k <= j; ++k) P1[tot++] = T[0][k];
                for(int k = 0; k < tot; ++k) P2[k] = P1[tot - 1 - k];
                bool flg = true;
                getFail(P1, tot, f1);
                getFail(P2, tot, f2);
                for(int k = 1; k < n; ++k) {
                    if(!find(T[k], P1, tot, f1) && !find(T[k], P2, tot, f2)) {
                        flg = false;
                        break;
                    }
                }
                if(flg) ans = j - i + 1;
            }
        }
        cout << ans << endl;
    }
}

N
暴力KMP匹配

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int maxn = 200 + 5;
void getFail(char* p, int m, int* f) {
    f[0] = 0, f[1] = 0;
    for(int i = 1; i < m; ++i) {
        int j = f[i];
        while(j && p[i] != p[j]) j = f[j];
        f[i + 1] = p[i] == p[j] ? j + 1 : 0;
    }
}
bool find(char* t, char* p, int m, int* f) {
    int n = strlen(t);
    getFail(p, m, f);
    int j = 0;
    int ans = 0;
    for(int i = 0; i < n; ++i) {
        while(j && p[j] != t[i]) j = f[j];
        if(p[j] == t[i]) j++;
        if(j == m) return true;
    }
    return false;
}
char P[maxn], T[4005][maxn];
int f[maxn];
char sub[maxn];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    int n;
    while(cin >> n) {
        if(n == 0) break;
        for(int i = 0; i < n; ++i) 
            cin >> T[i];
        int m = strlen(T[0]), tot;
        int ans = 0;
        for(int i = 0; i < m; ++i) {
            for(int j = i + (ans == 0 ? 0 : ans - 1); j < m; ++j) {
                tot = 0;
                for(int k = i; k <= j; ++k) P[tot++] = T[0][k];
                bool flg = true;
                getFail(P, tot, f);
                for(int k = 1; k < n; ++k) {
                    if(!find(T[k], P, tot, f)) {
                        flg = false;
                        break;
                    }
                }
                if(flg && ans < j - i + 1) {
                    ans = j - i + 1;
                    for(int k = 0; k < ans; ++k) {
                        sub[k] = T[0][i + k];
                    }
                } 
                else if(flg && ans == j - i + 1) {
                    bool ex = false;
                    for(int k = 0; k < ans; ++k) {
                        if(T[0][i + k] < sub[k]) {
                            ex = true;
                            break;
                        }
                        else if(T[0][i + k] > sub[k]) {
                            ex = false;
                            break;
                        }
                    }
                    if(ex) 
                        for(int k = 0; k < ans; ++k) sub[k] = T[0][i + k];
                }
            }
        }
        if(ans == 0) cout << "IDENTITY LOST" << endl;
        else {
            for(int i = 0; i < ans; ++i) cout << sub[i];
            cout << endl;
        }
    }
}

O
能出现多少次,就要看循环节有多少个了
再就是求最大最小表示

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
#include <queue>
#include <map>
using namespace std;
const int maxn = 1000000 + 5;

void getFail(char *p, int *f) {
    int m = strlen(p);
    f[0] = 0, f[1] = 0;
    for(int i = 1; i < m; ++i) {
        int j = f[i];
        while(j && p[i] != p[j]) j = f[j];
        f[i + 1] = p[i] == p[j] ? j + 1 : 0;
    }
}
int get_minstring(char *s) {
    int len = strlen(s);
    int i = 0, j = 1, k = 0;
    while(i < len && j < len && k < len) {
        int t = s[(i + k) % len] - s[(j + k) % len];
        if(t == 0) k++;
        else {
            if(t > 0) i = i + k + 1;
            else j = j + k + 1;
            if(i == j) j++;
            k = 0;
        }
    }
    return min(i, j);
}
int get_maxstring(char *s) {
    int len = strlen(s);
    int i = 0, j = 1, k = 0;
    while(i < len && j < len && k < len) {
        int t = s[(i + k) % len] - s[(j + k) % len];
        if(t == 0) k++;
        else {
            if(t > 0) j = j + k + 1;
            else i = i + k + 1;
            if(i == j) j++;
            k = 0;
        }
    }
    return min(i, j);
}
int f[maxn];
char str[maxn];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    while(cin >> str) {
        getFail(str, f);
        int len = strlen(str);
        int cnt = len / (len - f[len]);
        cout << get_minstring(str) + 1 << " " << cnt << " " << get_maxstring(str) + 1 << " " << cnt << endl;
    }
}

P
转换为最小表示,用set维护

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
#include <queue>
#include <map>
#include <set>
using namespace std;
int get_minstring(string s) {
    int len = s.length();
    int i = 0, j = 1, k = 0;
    while(i < len && j < len && k < len) {
        int t = s[(i + k) % len] - s[(j + k) % len];
        if(t == 0) k++;
        else {
            if(t > 0) i = i + k + 1;
            else j = j + k + 1;
            if(i == j) j++;
            k = 0;
        }
    }
    return min(i, j);
}
set<string> mp;
string str;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    int n;
    while(cin >> n) {
        mp.clear();
        for(int i = 0; i < n; ++i) {
            cin >> str;
            int len = str.length();
            int pos = get_minstring(str);
            string tmp = "";
            for(int i = 0; i < len; ++i) {
                tmp += str[(i + pos) % len];
            }
            if(mp.find(tmp) == mp.end()) mp.insert(tmp);
        }
        cout << mp.size() << endl;
    }
}

Q
exkmp

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
#include <queue>
#include <map>
#include <set>
using namespace std;
const int maxn = 1000000 + 5;
void pre_EKMP(char x[], int m, int next[]){
    next[0] = m;
    int j = 0;
    while( j+1 < m && x[j] == x[j+1] )j++; 
    next[1] = j;
    int k = 1;
    for(int i = 2; i < m; i++){
        int p = next[k] + k - 1;
        int L = next[i - k];
        if(i + L < p + 1) next[i] = L;
        else {
            j = max(0, p - i + 1);
            while( i+j < m && x[i+j] == x[j])j++; next[i] = j;
            k = i;
        }
    }
}
void EKMP(char x[], int m, char y[], int n, int next[], int extend[]) { //计算y的后缀与x的最长前缀
    pre_EKMP(x, m, next);
    int j = 0;
    while(j < n && j < m && x[j] == y[j]) j++; 
    extend[0] = j;
    int k = 0;
    for(int i = 1;i < n;i++){
        int p = extend[k]+k-1;
        int L = next[i-k];
        if(i+L < p+1)extend[i] = L;
        else {
            j = max(0,p - i + 1);
            while( i+j < n && j < m && y[i+j] == x[j] )j++; 
            extend[i] = j;
            k = i;
        }
    }
}
char str[maxn];
int f[maxn], ex[maxn];
vector<int> ans;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    int t;
    cin >> t;
    for(int cas = 1; cas <= t; ++cas) {
        cin >> str;
        int n = strlen(str);
        EKMP(str, n, str, n, f, ex);
        ans.clear();
        for(int i = 1; i < n; ++i) {
            if(ex[i] + i == n) ans.push_back(i);
        }
        cout << "Case #" << cas << ": " << ans.size() + 1 << endl;
        for(int i = 0; i < ans.size(); ++i) cout << ans[i] << " ";
        cout << n << endl;
    }
}

S
exkmp去求是否为回文串,若i位置时i+ex[i]=n表示后面的串形成回文串

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
#include <queue>
#include <map>
#include <set>
using namespace std;
const int maxn = 500000 + 5;
void pre_EKMP(char x[], int m, int next[]){
    next[0] = m;
    int j = 0;
    while( j+1 < m && x[j] == x[j+1] )j++; 
    next[1] = j;
    int k = 1;
    for(int i = 2; i < m; i++){
        int p = next[k] + k - 1;
        int L = next[i - k];
        if(i + L < p + 1) next[i] = L;
        else {
            j = max(0, p - i + 1);
            while( i+j < m && x[i+j] == x[j])j++; next[i] = j;
            k = i;
        }
    }
}
void EKMP(char x[], int m, char y[], int n, int next[], int extend[]) { //计算y的后缀与x的最长前缀
    pre_EKMP(x, m, next);
    int j = 0;
    while(j < n && j < m && x[j] == y[j]) j++; 
    extend[0] = j;
    int k = 0;
    for(int i = 1;i < n;i++){
        int p = extend[k]+k-1;
        int L = next[i-k];
        if(i+L < p+1)extend[i] = L;
        else {
            j = max(0,p - i + 1);
            while( i+j < n && j < m && y[i+j] == x[j] )j++; 
            extend[i] = j;
            k = i;
        }
    }
}
int val[30];
char S[maxn], T[maxn];
int f[maxn], ex1[maxn], ex2[maxn];
int sum[maxn];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    int t;
    cin >> t;
    while(t--) {
        for(int i = 0; i < 26; ++i) cin >> val[i];
        cin >> S;
        int n = strlen(S);
        for(int i = 0; i < n; ++i) {
            T[n - 1 - i] = S[i];
            sum[i + 1] = sum[i] + val[S[i] - 'a'];
        }
        EKMP(T, n, S, n, f, ex1);
        EKMP(S, n, T, n, f, ex2);
        int ans = -0x3f3f3f3f;
        for(int i = 1; i < n; ++i) {
            int tmp = 0;
            if(i + ex1[i] == n) tmp += sum[n] - sum[i];
            if(n - i + ex2[n - i] == n) tmp += sum[i];
            //cout << "i = " << i << ", ex1 = " << ex1[i] << ", ex2 = " << ex2[n - i] << endl; 
            ans = max(tmp, ans);
        }
        cout << ans << endl;
    }
}

T
分类讨论 + trie + exkmp

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 2000005;
const int KIND = 26;

struct TrieNode
{
    int num;    // 到当前节点的字符串个数
    int cnt;    // 当前节点后面回文子串个数
    TrieNode* nxt[26];
};

TrieNode node[MAXN];
TrieNode* root;           // trie树的根节点
int bg[MAXN];             // bg[i]第i+1个字符串开始的位置
int ed[MAXN];             // ed[i]第i+1个字符串结束的位置
bool flag[2][MAXN];       // flag[0][i]为true表示原串后面为回文串   flag[1][i]表示反串
char S[MAXN];             // 存放原串
char T[MAXN];             // 存放反串
int nxt[MAXN];            // 存放next数组
int extend[MAXN];         // 用于判断是否为回文子串
LL ans;                   // 保存结果
int tot;                  // node数组的下标

void GetNext(char* T, int lhs, int rhs)
{
    int j = 0;
    while (lhs + j + 1 <= rhs && T[lhs + j] == T[lhs + j + 1]) ++j;
    nxt[lhs + 1] = j;
    int k = lhs + 1;
    for (int i = lhs + 2; i <= rhs; ++i)
    {
        int p = nxt[k] + k - 1;
        int L = nxt[lhs + i - k];
        if (L + i < p + 1) nxt[i] = L;
        else
        {
            j = max(0, p - i + 1);
            while (i + j <= rhs && T[lhs + j] == T[i + j]) ++j;
            nxt[i] = j;
            k = i;
        }
    }
}

void ExtendKMP(char* S, char* T, int lhs, int rhs, bool sign)
{
    GetNext(T, lhs, rhs);
    int j = 0;
    while (j + lhs <= rhs && S[j + lhs] == T[j + lhs]) ++j;
    extend[lhs] = j;
    int k = lhs;
    for (int i = lhs + 1; i <= rhs; ++i)
    {
        int p = extend[k] + k - 1;
        int L = nxt[lhs + i - k];
        if (L + i < p + 1) extend[i] = L;
        else
        {
            j = max(0, p - i + 1);
            while (i + j <= rhs && S[i + j] == T[lhs + j]) ++j;
            extend[i] = j;
            k = i;
        }
    }
    for (int i = lhs; i <= rhs; ++i)
    {
        if (extend[i] == rhs - i + 1)
            flag[sign][i] = true;
    }
}

void Insert(char S[], int lhs, int rhs)
{
    TrieNode* temp = root;
    for (int i = lhs; i <= rhs; ++i)
    {
        int ch = S[i] - 'a';
        temp->cnt += flag[0][i];    // 更新当前节点后面回文子串的数目
        if (temp->nxt[ch] == NULL) temp->nxt[ch] = &node[tot++];
        temp = temp->nxt[ch];
    }
    ++temp->num; // 更新到当前节点的字符串数目
}

void Search(char S[], int lhs, int rhs)
{
    TrieNode* temp = root;
    for (int i = lhs; i <= rhs; ++i)
    {
        int ch = S[i] - 'a';
        temp = temp->nxt[ch];
        if (temp == NULL) break;
        if ((i < rhs && flag[1][i + 1]) || i == rhs)
            ans += temp->num;
    }
    if (temp) ans += temp->cnt;
}

int main()
{
    int n;
    while (scanf("%d", &n) != EOF)
    {
        // 初始化
        tot = 0;
        ans = 0;
        memset(node, 0, sizeof(node));
        memset(flag, 0, sizeof(flag));
        root = &node[tot++];

        int l = 0;
        int L = 0;
        for (int i = 0; i < n; ++i)
        {
            // 输入一组数据
            scanf("%d", &l);
            scanf("%s", S + L);

            // 生成反串
            for (int j = 0; j < l; ++j)
                T[L + j] = S[L + l - 1 - j];

            bg[i] = L;
            ed[i] = L + l - 1;


            ExtendKMP(S, T , bg[i], ed[i], 0);
            ExtendKMP(T, S , bg[i], ed[i], 1);
            Insert(S, bg[i], ed[i]);

            L += l;
        }

        for (int i = 0; i < n; ++i)
            Search(T, bg[i], ed[i]);

        printf("%lld\n", ans);
    }
    return 0;
}

U
Manacher算法板子

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int maxn = 1000000 + 5;

char str[maxn], s[maxn << 1];
int pal[maxn << 1], len;
void build() {
    s[0] = '@';
    s[1] = '#';
    len = strlen(str);
    for(int i = 0; i < len; ++i) {
        s[2 * i + 2] = str[i];
        s[2 * i + 3] = '#';
    }
    s[2 * len + 2] = '\0';
}
void manacher() {
    build();
    int mx = 0, id;
    len = 2 * len + 2;
    for(int i = 0; i < len; ++i) {
        if(mx > i) pal[i] = min(pal[2 * id - i], mx - i);
        else pal[i] = 1;
        for(; s[i - pal[i]] == s[i + pal[i]]; pal[i]++)
        if(pal[i] + i > mx) {
            mx = pal[i] + i;
            id = i;
        }
    }
}

int main()
{
    int cas = 1;
    while(~scanf("%s", &str)) {
        if(str[0] == 'E') break;
        manacher();
        int ans = 0;
        for(int i = 0; i < len; ++i) {
            ans = max(ans, pal[i]);
        }
        printf("Case %d: %d\n", cas++, ans - 1);
    }
}

V
Manacher魔改题
对pal数组更新时加入递减判断条件

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100000 + 5;

int f[maxn], fx[maxn << 1];
int pal[maxn << 1], n, m;
void build() {
    fx[0] = -1;
    fx[1] = 300;
    for(int i = 0; i < n; ++i) {
        fx[2 * i + 2] = f[i];
        fx[2 * i + 3] = 300;
    }
}
int manacher() {
    build();
    int mx = 0, id, ans = 0, k;
    m = 2 * n + 2;
    for(int i = 0; i < m; ++i) {
        if(mx > i) pal[i] = min(pal[2 * id - i], mx - i);
        else pal[i] = 1;
        k = fx[i];
        while(fx[i - pal[i]] == fx[i + pal[i]] && ((fx[i - pal[i]] == 300) || (fx[i - pal[i]] <= k))) {
            if(fx[i - pal[i]] != 300) k = fx[i - pal[i]];
            pal[i]++;
        }
        if(pal[i] + i > mx) {
            mx = pal[i] + i;
            id = i;
        }
        ans = max(ans, pal[i]);
    }
    return ans - 1;
}

int main()
{
    int t;
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);
        for(int i = 0; i < n; ++i) scanf("%d", &f[i]);
        if(n == 1) {
            printf("1\n");
            continue;
        }
        printf("%d\n", manacher());
    }
}

W
manacher

#include <bits/stdc++.h>
using namespace std;
const int maxn = 200000 + 5;
char str[maxn], s[maxn << 1];
int pal[maxn << 1], mapp[30], n;
void build() {
    s[0] = '@';
    s[1] = '#';
    for(int i = 0; i < n; ++i) {
        s[2 * i + 2] = str[i];
        s[2 * i + 3] = '#';
    }
    s[2 * n + 2] = '\0';
}
void manacher() {
    build();
    int mx = 0, id;
    n = 2 * n + 2;
    for(int i = 0; i < n; ++i) {
        if(mx > i) pal[i] = min(pal[2 * id - i], mx - i);
        else pal[i] = 1;
        for(; s[i - pal[i]] == s[i + pal[i]]; pal[i]++)
        if(pal[i] + i > mx) {
            mx = pal[i] + i;
            id = i;
        }
    }
}

int main()
{
    char tar[5];
    while(~scanf("%s", &tar)) {
        scanf("%s", &str);
        int now = 0, noww = 0;
        for(int i = tar[0] - 'a'; i + 'a' <= 'z'; ++i) {
            mapp[i] = now;
            now++;
        }
        while(now + 'a' <= 'z') {
            mapp[noww++] = now++;
        }
        n = strlen(str);
        for(int i = 0; i < n; ++i) str[i] = mapp[str[i] - 'a'] + 'a';
        manacher();
        int pos = -1, len = 0;
        for(int i = 0; i < n; ++i) {
            if(pal[i] - 1 > len) {
                len = pal[i] - 1;
                pos = i;
            }
        }
        if(len < 2) {
            puts("No solution!");
            continue;
        }
        int l = s[pos] == '#' ? ((pos - 3) / 2 - len / 2 + 1) : ((pos - 2) / 2 - len / 2);
        int r = l + len - 1;
        printf("%d %d\n", l, r);
        for(int i = l; i <= r; ++i) putchar(str[i]);
        puts("");
    }
}

Z
求给的字符串中的字串需要满足为前后缀且当中还需要出现的最长长度

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;
void getFail(char* p, int m, int* f) {
    f[0] = 0, f[1] = 0;
    for(int i = 1; i < m; ++i) {
        int j = f[i];
        while(j && p[i] != p[j]) j = f[j];
        f[i + 1] = p[i] == p[j] ? j + 1 : 0;
    }
}
int find(char* t, int l, int r, char* p, int m, int* f) {
    getFail(p, m, f);
    int j = 0;
    for(int i = l; i < r; ++i) {
        while(j && p[j] != t[i]) j = f[j];
        if(p[j] == t[i]) j++;
        if(j == m) return i - m + 1;
    }
    return -1;
}
char str[maxn];
int f[maxn];
int main()
{
    int t;
    scanf("%d", &t);
    while(t--) {
        scanf("%s", &str);
        int n = strlen(str);
        getFail(str, n, f);
        int k = f[n], ans = 0;
        while(k) {
            if(k * 2 >= n) {
                k = f[k];
                continue;
            }
            if(find(str, k, n - k, str, k, f) != -1) {
                ans = k;
                break;
            }
            k = f[k];
        }
        printf("%d\n", ans);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值