zoukankan      html  css  js  c++  java
  • 《挑战程序设计竞赛》 利用后缀数组求最长回文串

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

    解决方案:利用后缀数组,设这个字符串是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
    ",ans);
        }
        return 0;
    }
    //mississippi
  • 相关阅读:
    软件测试培训第9天
    软件培训第8天
    软件测试培训第7天
    软件测试培训第5天
    软件测试培训第6天
    软件测试培训第4天
    软件测试培训第3天
    MySQL复杂用法
    MySQL的基本语法
    VM虚拟机上安装Redhat
  • 原文地址:https://www.cnblogs.com/ZefengYao/p/9104008.html
Copyright © 2011-2022 走看看