《挑战程序设计竞赛》 利用后缀数组求最长回文串

问题:求一个字符串的最长回文子串。

解决方案:利用后缀数组,设这个字符串是S,S'是这个字符串的反转,设T=S+'$'+S',则所有的回文子串一定会出现在字符串T当中的其中两个后缀的前缀上,所以只要求T的lcp,利用lcp求后缀之间的相同前缀的最大长度。

代码参考:

#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>
#include<string>
#include<cmath>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N_MAX = 100000 + 20;
typedef long long ll;
int n, k;
int Rank[N_MAX * 2];
int tmp[N_MAX * 2];
int sa[N_MAX * 2];
int lcp[N_MAX * 2];
bool compare_sa(const int& i, const int& j) {
    if (Rank[i] != Rank[j])return Rank[i] < Rank[j];
    else {
        int ri = i + k <= n ? Rank[i + k] : -1;
        int rj = j + k <= n ? Rank[j + k] : -1;
        return ri < rj;
    }
}

void construct_sa(const string& S, int *sa) {
    n = S.size();
    for (int i = 0; i <= n; i++) {
        sa[i] = i;
        Rank[i] = i < n ? S[i] : -1;
    }
    for (k = 1; k <= n; k *= 2) {
        sort(sa, sa + n + 1, compare_sa);
        tmp[sa[0]] = 0;
        for (int i = 1; i <= n; i++) {
            tmp[sa[i]] = tmp[sa[i - 1]] + (compare_sa(sa[i - 1], sa[i]) ? 1 : 0);
        }
        for (int i = 0; i <= n; i++) {
            Rank[i] = tmp[i];
        }
    }
}
void construct_lcp(const string& S, int *sa, int *lcp) {
    memset(lcp, 0, sizeof(lcp));
    int n = S.length();
    for (int i = 0; i <= n; i++)Rank[sa[i]] = i;
    int h = 0;
    lcp[0] = 0;
    for (int i = 0; i < n; i++) {
        int j = sa[Rank[i] - 1];
        if (h > 0)h--;
        for (; j + h < n&&i + h < n; h++) {
            if (S[j + h] != S[i + h])break;
        }
        lcp[Rank[i] - 1] = h;
    }
}

int min_lcp[N_MAX * 2];
void rmq_init(int k, int l, int r) {//节点k的区间[l,r)
    if (r <= l)return;
    if (r - l == 1) {
        min_lcp[k] = lcp[l];
    }
    else {
        int chl =2*k + 1, chr = 2*k + 2;
        int mid = (l + r) >> 1;
        rmq_init(chl, l, mid);
        rmq_init(chr, mid, r);
        min_lcp[k] = min(min_lcp[chl], min_lcp[chr]);
    }
}

int query_rmq(int a, int b, int k, int l, int r) {
    if (b <= l || a >= r) {
        return INF;
    }
    else if (a <= l&&b >= r) {
        return min_lcp[k];
    }
    else {
        int mid = (l + r) >> 1;
        int min_L = query_rmq(a, b,2*k + 1, l, mid);
        int min_R = query_rmq(a, b, 2*k + 2, mid, r);
        return min(min_L, min_R);
    }
}

string s;
int main() {
    while (cin >> s) {
        int N = s.size();
        string T = s;
        reverse(T.begin(), T.end());
        s += '$' + T;
        n = s.size();
        construct_sa(s, sa);
        construct_lcp(s, sa, lcp);
        for (int i = 0; i <= s.size(); i++)Rank[sa[i]] = i;
        rmq_init(0,0,s.size()+1);
        int ans = 0;
        for (int i = 0; i < N; i++) {
            int j = 2 * N - i;
            int l = query_rmq(min(Rank[i], Rank[j]), max(Rank[i], Rank[j]), 0, 0, s.size()+1);
            cout <<"l:"<< l << endl;
            ans = max(ans,2*l-1);
        }
        for (int i = 1; i < N;i++) {
            int j = 2 * N - i + 1;
            int l = query_rmq(min(Rank[i], Rank[j]), max(Rank[i], Rank[j]), 0, 0, s.size()+1);
            cout << "l:" << l << endl;
            ans = max(ans, 2 * l);
        }
        printf("%d\n",ans);
    }
    return 0;
}
//mississippi

 

posted on 2018-05-29 11:05  ZefengYao  阅读(343)  评论(0编辑  收藏  举报

导航