POJ 2217 (后缀数组+最长公共子串)

题目链接http://poj.org/problem?id=2217

题目大意: 求两个串的最长公共子串,注意子串是连续的,而子序列可以不连续。

解题思路

有个炒鸡快的O(n)的Manacher算法。不过只能求裸的最长公共和回文子串。

后缀数组解法是这类问题的模板解法。

对于n个串的最长公共子串,这要把这些串连在一起,中间用"$"这类的特殊符号分隔一下。

先求后缀数组,再求最长公共前缀,取相邻两个且属于不同串的sa的最大LCP即可。

原理就是:这样把分属两个串的LCP都跑了一遍,也就是相当于把所有子串走了一遍,

只不过走这些子串是经过层层预处理过的。

下面提供一个使用的string模板,稍微稳定点。

#include "cstring"
#include "cstdio"
#include "string"
#include "iostream"
using namespace std;
#define maxn 30000
struct Suffix
{
    int r[maxn];
    int sa[maxn],rank[maxn],height[maxn];
    int t[maxn],t2[maxn],c[maxn],n,m;
    void init(string s)
    {
        n=s.size();
        for(int i=0;i<n;i++) r[i]=(int)s[i];
        m=128;
    }
    int cmp(int *r,int a,int b,int l) {return r[a]==r[b]&&r[a+l]==r[b+l];}
    void build()
    {
        int i,k,p,*x=t,*y=t2;
        r[n++]=0;
        for (i=0; i<m; i++) c[i]=0;
        for (i=0; i<n; i++) c[x[i]=r[i]]++;
        for (i=1; i<m; i++) c[i]+=c[i-1];
        for (i=n-1; i>=0; i--) sa[--c[x[i]]]=i;
        for (k=1,p=1; k<n; k*=2,m=p)
        {
            for (p=0,i=n-k; i<n; i++) y[p++]=i;
            for (i=0; i<n; i++) if (sa[i]>=k) y[p++]=sa[i]-k;
            for (i=0; i<m; i++) c[i]=0;
            for (i=0; i<n; i++) c[x[y[i]]]++;
            for (i=1; i<m; i++) c[i]+=c[i-1];
            for (i=n-1; i>=0; i--) sa[--c[x[y[i]]]]=y[i];
            swap(x,y);
            p=1;
            x[sa[0]]=0;
            for (i=1; i<n; i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],k)?p-1:p++;
        }
        n--;
    }
    void LCP()
    {
        int i,j,k=0;
        for (i=1; i<=n; i++) rank[sa[i]]=i;
        for (i=0; i<n; i++)
        {
            if (k) k--;
            j=sa[rank[i]-1];
            while (r[i+k]==r[j+k]) k++;
            height[rank[i]]=k;
        }
    }
    int LCS(string s1,string s2)
    {
        int len=s1.size();
        s1=s1+"$"+s2;
        init(s1);
        build();
        LCP();
        int ans=0;
        for(int i=2;i<=n;i++)
            if((sa[i-1]<len)!=(sa[i]<len)) ans=max(ans,height[i]);
        return ans;
    }
};

int main()
{
    //freopen("in.txt","r",stdin);
    ios::sync_with_stdio(false);
    int T;
    string s,t;
    cin>>T;getline(cin,s);
    while(T--)
    {
        getline(cin,s);
        getline(cin,t);
        Suffix a;
        cout<<"Nejdelsi spolecny retezec ma delku "<<a.LCS(s,t)<<"."<<endl;
    }
}
13557348 neopenx 2217 Accepted 1060K 32MS C++ 2109B 2014-10-23 10:35:07
时间: 2024-10-12 16:36:54

POJ 2217 (后缀数组+最长公共子串)的相关文章

[poj 2274]后缀数组+最长公共子序列

题目链接:http://poj.org/problem?id=2774 后缀数组真的太强大了,原本dp是n^2的复杂度,在这里只需要O(n+m). 做法:将两个串中间夹一个未出现过的字符接起来,然后做一次后缀数组,得到的height相邻两个排名的后缀,在串中的位置如果满足在分界符左右两侧,就更新最长公共前缀.最后得到的最大值就是最长公共子序列. #include<algorithm> #include<cstdio> #include<cstring> using na

POJ 2774 (后缀数组 最长公共字串) Long Long Message

用一个特殊字符将两个字符串连接起来,然后找最大的height,而且要求这两个相邻的后缀的第一个字符不能在同一个字符串中. 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 6 const int maxn = 200000 + 10; 7 8 char s[maxn]; 9 int n; 10 int sa[maxn], rank[maxn],

POJ 3294 Life Forms [最长公共子串加强版 后缀数组 &amp;&amp; 二分]

题目:http://poj.org/problem?id=3294 Life Forms Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 18549   Accepted: 5454 Description You may have wondered why most extraterrestrial life forms resemble humans, differing by superficial traits s

CSU1632Repeated Substrings(后缀数组/最长公共前缀)

题意就是求一个字符串的重复出现(出现次数>=2)的不同子串的个数. 标准解法是后缀数组.最长公共前缀的应用,对于样例aabaab,先将所有后缀排序: aab 3    aabaab 1    ab 2    abaab 0    b 1    baab 每个后缀前面数字代表这个后缀与它之前的后缀(rank比它小1)的最长公共前缀的长度:然而就可以这样理解这个最长公共前缀LCP.aabaab与aab的最长公共前缀是3,那说明子串a.aa.aab都至少出现的两次,那么这就是后缀aab重复出现的子串个

面试题[后缀数组]: 最长重复子串

题目:给定一个字符串,求出最长重复子串. 这个题目可以用后缀数组来解:对后缀数组排好序,这样重复的子串就在相邻的后缀中找就可以了.我的C++代码实现如下: class Solution { public: string LongestRepeatingSubstring(string str) { size_t len = str.size(); vector<string> SuffixArray(len); for (size_t i = 0; i < len; ++i) Suffi

POJ 2774 后缀数组:求最长公共子串

思路:其实很简单,就是两个字符串连接起来,中间用个特殊字符隔开,然后用后缀数组求最长公共前缀,然后不同在两个串中,并且最长的就是最长公共子串了. 注意的是:用第一个字符串来判断是不是在同一个字符中,刚开始用了第二个字符的长度来判断WA了2发才发现. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #include<

poj 2774 最长公共子串--字符串hash或者后缀数组或者后缀自动机

http://poj.org/problem?id=2774 想用后缀数组的看这里:http://blog.csdn.net/u011026968/article/details/22801015 本文主要讲下怎么hash去找 开始的时候写的是O(n^2 logn)算法 果断超时...虽然也用了二分的,, 代码如下: //hash+二分 #include <cstdio> #include <cstring> #include <algorithm> #include

后缀数组(多个字符串的最长公共子串)—— POJ 3294

对应POJ 题目:点击打开链接 Life Forms Time Limit:6666MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Status Description Problem C: Life Forms You may have wondered why most extraterrestrial life forms resemble humans, differing by superficial tra

poj 2774 Long Long Message,后缀数组,求最长公共子串 hdu1403

题意:给出两个字符串,求最长公共子串的长度. 题解:首先将两个字符串连在一起,并在中间加一个特殊字符(字串中不存在的)分割,然后两个串的最长公共字串就变成了所有后缀的最长公共前缀.这时就要用到height数组,因为任意两个后缀的公共前缀必定是某些height值中的最小值,而这个值如果最大则一定是height中的最大值.在此题中还要注意height最大一定要在两个值所代表的后缀分属不同的字符串地前提下. #include<cstdio> #include<cstring> #incl