SPOJ 1811 Longest Common Substring(求两个串的最长公共子串)

http://www.spoj.com/problems/LCS/

题目:求两个串的最长公共子串

分析:

以A建立SAM 让B在SAM上匹配可以类比于kmp思想,我们知道在Parent树上,fa是当前节点的子集,也就是说满足最大前缀,利用这个就可以做题了

#include <bits/stdc++.h>
#define LL long long
#define P pair<int, int>
#define lowbit(x) (x & -x)
#define mem(a, b) memset(a, b, sizeof(a))
#define rep(i, a, n) for (int i = a; i <= n; ++i)
const int maxn =1000005;
#define mid ((l + r) >> 1)
#define lc rt<<1
#define rc rt<<1|1
using namespace std;
string str1,str2;
struct SAM{

    int trans[maxn<<1][26], slink[maxn<<1], maxlen[maxn<<1];
    int last, now, root, len;
    inline void newnode (int v) {
        maxlen[++now] = v;
    }

    inline void extend(int c) {
        newnode(maxlen[last] + 1);
        int p = last, np = now;
        // 更新trans
        while (p && !trans[p][c]) {
            trans[p][c] = np;
            p = slink[p];
        }
        if (!p) slink[np] = root;
        else {
            int q = trans[p][c];
            if (maxlen[p] + 1 != maxlen[q]) {
                // 将q点拆出nq,使得maxlen[p] + 1 == maxlen[q]
                newnode(maxlen[p] + 1);
                int nq = now;
                memcpy(trans[nq], trans[q], sizeof(trans[q]));
                slink[nq] = slink[q];
                slink[q] = slink[np] = nq;
                while (p!=-1 && trans[p][c] == q) {
                    trans[p][c] = nq;
                    p = slink[p];
                }
            }else slink[np] = q;
        }
        last = np;
        // 初始状态为可接受状态

    }
   inline void init()
   {
       memset(trans,0,sizeof(trans));
       memset(slink,0,sizeof(slink));
       memset(maxlen,0,sizeof(maxlen));
       root = last=now=1;
   }
   inline void build(string s)
   {
       len=s.size();
       for(int i=0 ; i<len ; i++)
       extend(s[i]-‘a‘);
   }
   inline int work(string s)
   {
       int Len=s.size();
       int t1=0;
       int ret=0;
       int now=root;
       for(int i=0 ; i<Len ; i++)
       {
           int ind=s[i]-‘a‘;
           while(now!=0 && trans[now][ind]==0)
           {
               now=slink[now];
               if(now!=0) t1=maxlen[now];
           }
           if(now==0)
           {
               now=root ; t1=0;
           }
           else
           {
               now=trans[now][ind];
               t1++;
               ret=max(ret,t1);
           }
       }
       return ret;
   }

}sam;

int main()
{
    sam.init();
   cin>>str1>>str2;
   sam.build(str1);
   printf("%d\n",sam.work(str2));
}

时间复杂的O(n)

 

原文地址:https://www.cnblogs.com/shuaihui520/p/10686862.html

时间: 2024-11-05 16:04:03

SPOJ 1811 Longest Common Substring(求两个串的最长公共子串)的相关文章

求两个字符串的最长公共子串——Java实现

要求:求两个字符串的最长公共子串,如"abcdefg"和"adefgwgeweg"的最长公共子串为"defg"(子串必须是连续的) public class Main03{ // 求解两个字符号的最长公共子串 public static String maxSubstring(String strOne, String strTwo){ // 参数检查 if(strOne==null || strTwo == null){ return null

SPOJ 1811 Longest Common Substring

Description 给出两个字符串,求最长公共子串. Sol SAM. 这题随便做啊...后缀数组/Hash+二分都可以. SAM就是模板啊...直接在SAM上跑就行,没有了 \(go[w]\) 就往 \(par\) 跳. 每走一步统计一下答案. Code #include<cstdio> #include<cstring> #include<iostream> using namespace std; const int N = 250005; struct St

●SPOJ 1811 Longest Common Substring

题链: http://poj.org/problem?id=2774 题解: 求两个字符串(S,T)的最长公共子串.对 S串建后缀自动机.接下来就用这个自动机去求出能和 S串匹配的 T的每一个前缀的最长的后缀.最终答案就是对每个 T的前缀得到的答案取最大值就好了. 代码: #include<cstdio> #include<cstring> #include<iostream> #define MAXN 250050 #define filein(x) freopen(

[URAL-1517][求两个字符串的最长公共子串]

Freedom of Choice URAL - 1517 Background Before Albanian people could bear with the freedom of speech (this story is fully described in the problem "Freedom of speech"), another freedom - the freedom of choice - came down on them. In the near fu

【java】求两个字符串的最长公共子串

这个是华为OJ上的一道题目.首先,如果我们用java写代码,华为OJ有以下三条规则需遵守,否则编译无法通过或者用例无法通过,规则如下: (1)一定不可以有包名: (2)主类名只能为Main: (3)不可以输出与结果无关的信息. 好了,按照以上规则,我们写出来的代码如下(此代码不是最优的,只是用来记录华为OJ上java代码的书写规则): import java.util.Scanner; public class Main { public static void main(String[] ar

求两个字符串的最长公共子串

public static String findLongestOfTheSame(String s1,String s2) { char[] c1=s1.toCharArray(); char[] c2=s2.toCharArray(); int l1=c1.length; int l2=c2.length; int count=0,maxLength=0,start=0,end=0; boolean hasTheSame=false; for(int i=0;i<l1;i++) { coun

POJ 2774 求两个串的最长公共前缀 | 后缀数组

#include<cstdio> #include<algorithm> #include<cstring> #define N 200005 using namespace std; int buf1[N],buf2[N],sa[N],rnk[N],buc[N],n,height[N],ans,belong[N]; char s[N]; void suffix_sort() { int *x=buf1,*y=buf2,m=1000; for (int i=0;i<

【SPOJ】Longest Common Substring

[SPOJ]Longest Common Substring 求两个字符串的最长公共子串 对一个串建好后缀自动机然后暴力跑一下 废话 讲一下怎么跑吧 从第一个字符开始遍历,遍历不到了再沿着\(parents\)走看能否找到出路,走到某个点时,统计一下走过了多少点然后更新答案 来说说这样做的正确性: 遍历是肯定的, PAM 从根节点出发的任意路径都表示一个子串 沿着\(parents\)边往后走,保证贪心情况下维护最长公共子串寻找出路 注意这里是统计走过了多少点更新答案,不能直接通过\(len\)

【SPOJ】Longest Common Substring(后缀自动机)

[SPOJ]Longest Common Substring(后缀自动机) 题面 Vjudge 题意:求两个串的最长公共子串 题解 \(SA\)的做法很简单 不再赘述 对于一个串构建\(SAM\) 另外一个串在\(SAM\)上不断匹配 最后计算答案就好了 匹配方法: 如果\(trans(s,c)\)存在 直接沿着\(trans\)走就行,同时\(cnt++\) 否则沿着\(parent\)往上跳 如果存在\(trans(now,c),cnt=now.longest+1\) 否则,如果不存在可行的