718. Maximum Length of Repeated Subarray 后缀数组解最长公共子串 O(n log^2 n)时间复杂度

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/luke2834/article/details/82953779

题意

  • 找最长公共子串

思路

  • 用dp的方法很容易在O(n^2)解决问题,这里主要讨论用后缀数组的思路解决这个问题
  • 后缀数组里有两个经典的概念或者称为数据结构,就是后缀数组SA,以及高度数组LCP
  • SA数组的定义是:将原串S所有的后缀按字典序排序后,定义rank(i)为后缀S[i…]的排名,SA数组是rank数组的逆映射,即SA(rank(i)) = i
  • LCP数组的定义是:LCP(i)是后缀S[SA(i)…]和后缀S[SA(i+1)…]的最长公共前缀长度
  • 这里就不讨论这两个数组的求解算法了,我们使用比较简单的倍增法求解SA数组,复杂度是O(n log^2 n)的,有了SA数组,求解LCP数组是O(n)的
  • 有了LCP数组后,我们先来思考另一个问题,一个数组里两个不同的子串的最长公共子串是多长呢?答案是max(LCP),也就是LCP数组里的最大值。原因的话反证一下很简单,这里简单说明一下,主要是考虑数组其它子串和以i开头的子串的最长公共子串是多长,容易证明能达到最长的只能是以SA(rank(i)-1)或SA(rank(i)+1)开头的子串,那么这个结果都保存在LCP里了,所以遍历一遍LCP就能找到最大值
  • 利用上述结论,我们很容易解决新的问题了。可以把两个数组拼在一起,并在拼接处加一个特殊的int,是在两个数组里都没有出现的
  • 求出LCP数组后,我们只要找i和SA(i+1)不在同一个字符串的LCP的最大值即可

实现

class Solution {
public:
    //size of rank and sa are n+1
    //size of lcp is n, definition of lcp[i] is max common prefix of s[sa[i]...] and s[sa[i+1]...]
    //input s of getSa and getLcp can be string as well
    vector<int> rank, sa, lcp;
    void getSa(const vector<int>& s, vector<int>& rank, vector<int>& sa){
        int n = s.size();
        vector<int> tmp(n+1);
        for (int i = 0; i < n; i++){
            sa.push_back(i);
            rank.push_back(s[i]);
        }
        sa.push_back(n);
        rank.push_back(-1);
        for (int k = 1; k <= n; k <<= 1){
            auto cmp = [&](int x, int y){
                if (rank[x] != rank[y])
                    return rank[x] < rank[y];
                auto tx = x + k > n ? -1 : rank[x + k];
                auto ty = y + k > n ? -1 : rank[y + k];
                return tx < ty;
            };
            sort(sa.begin(), sa.end(), cmp);
            tmp[sa[0]] = 0;
            for (int i = 1; i <= n; i++){
                tmp[sa[i]] = tmp[sa[i-1]];
                if (cmp(sa[i-1], sa[i])){
                    tmp[sa[i]]++;
                }
            }
            for (int i = 0; i <= n; i++)
                rank[i] = tmp[i];
        }
    }
    void getLcp(const vector<int>& s, const vector<int>& rank, const vector<int>& sa,
               vector<int>& lcp){
        int n = s.size();
        lcp.insert(lcp.begin(), n, 0);
        for (int i = 0, h = 0; i < n; i++){
            if (h > 0)
                h--;
            int k = rank[i];
            int j = sa[k-1];
            while (max(j, i) + h < n && s[j+h] == s[i+h]){
                h++;
            }
            lcp[k-1] = h;
        }
    }
    int findLength(vector<int>& A, vector<int>& B) {
        int n = A.size(), m = B.size();
        A.push_back(101);
        A.insert(A.end(), B.begin(), B.end());
        getSa(A, rank, sa);
        getLcp(A, rank, sa, lcp);
        int ans = 0;
        for (int i = 0; i <= n + m; i++){
            if (sa[i] < n && sa[i+1] > n || sa[i] > n && sa[i+1] < n){
                ans = max(ans, lcp[i]);
            }
        }
        return ans;
    }
};

猜你喜欢

转载自blog.csdn.net/luke2834/article/details/82953779
今日推荐