J - Long Long Message (最长公共子串)

题目链接:https://cn.vjudge.net/contest/283743#problem/J

题目大意:给你两个字符串,问你两个字符串的最长的公共子串。

具体思路:把两个字符串合在一起,然后求后缀数组,按照排名之后的字符串,如果两个相邻的字符串的sa[i]和sa[i-1]分别属于两个字符串,那么这个就是题目允许的值之一,然后再从这些值里面找一个最大的输出就可以了。

AC代码:

  1 #include<iostream>
  2 #include<stack>
  3 #include<cstring>
  4 #include<iomanip>
  5 #include<stdio.h>
  6 #include<algorithm>
  7 #include<cmath>
  8 using namespace std;
  9 # define ll long long
 10 # define inf 0x3f3f3f3f
 11 const int maxn = 5e5+100;
 12 int cntA[maxn], cntB[maxn], sa[maxn], tsa[maxn], A[maxn], B[maxn], height[maxn];
 13 int Rank[maxn];
 14 char ch[maxn];
 15 char str1[maxn];
 16 int sto[maxn];
 17 ll n;
 18 //sa[i]代表第i小的后缀位置,Rank[i]代表第i位置的后缀,排名第几小
 19 // height[i]代表排名第i个字符串和第i-1个字符串的相同前缀有多少个
 20 void cal()
 21 {
 22     for(int i = 0; i < 256; i++)
 23         cntA[i] = 0;
 24     for(int i = 1; i <= n; i++)
 25     {
 26         cntA[ch[i-1]]++;
 27     }
 28     for(int i = 1; i < 256; i++)
 29         cntA[i] += cntA[i-1];
 30     for(int i = n; i; i--)
 31         sa[cntA[ch[i-1]]--] = i;
 32     Rank[sa[1]] = 1;
 33     for(int i = 2; i <= n; i++)
 34     {
 35         Rank[sa[i]] = Rank[sa[i-1]];
 36         if(ch[sa[i]-1] != ch[sa[i-1]-1])
 37             Rank[sa[i]]++;
 38     }
 39     for(int l = 1; Rank[sa[n]] < n; l <<= 1)
 40     {
 41         memset(cntA, 0, sizeof(cntA));
 42         memset(cntB, 0, sizeof(cntB));
 43         for(int i = 1; i <= n; i++)
 44         {
 45             cntA[A[i] = Rank[i]]++;
 46             cntB[B[i] = (i+l <= n)?Rank[i+l]:0]++;
 47         }
 48         for(int i = 1; i <= n; i++)
 49             cntB[i] += cntB[i-1];
 50         for(int i = n; i; i--)
 51             tsa[cntB[B[i]]--] = i;
 52         for(int i = 1; i <= n; i++)
 53             cntA[i] += cntA[i-1];
 54         for(int i = n; i; i--)
 55             sa[cntA[A[tsa[i]]]--] = tsa[i];
 56         Rank[sa[1]]=1;
 57         for(int i = 2; i <= n; i++)
 58         {
 59             Rank[sa[i]] = Rank[sa[i-1]];
 60             if(A[sa[i]] != A[sa[i-1]] || B[sa[i]] != B[sa[i-1]])
 61                 Rank[sa[i]]++;
 62         }
 63     }
 64     for(int i = 1, j = 0; i <= n; i++)
 65     {
 66         if(j)
 67             j--;
 68         while(ch[i+j-1] == ch[sa[Rank[i]-1] + j - 1])
 69             j++;
 70         height[Rank[i]] = j;
 71     }
 72 }
 73 int main()
 74 {
 75     scanf("%s",str1);
 76     int len1=strlen(str1);
 77     for(int i=0; i<len1; i++)
 78     {
 79         ch[i]=str1[i];
 80     }
 81     scanf("%s",str1);
 82     int len2=strlen(str1);
 83     for(int i=len1; i<len1+len2; i++)
 84     {
 85         ch[i]=str1[i-len1];
 86     }
 87     n=len1+len2;
 88     cal();
 89     int maxx=0;
 90     for(int i=2; i<=len1+len2; i++)
 91     {
 92         if(height[i]>maxx)
 93         {
 94             if(sa[i]>=1&&sa[i]<=len1&&sa[i-1]>=(1+len1)&&sa[i-1]<=len1+len2)
 95                 maxx=height[i];
 96             if(sa[i-1]>=1&&sa[i-1]<=len1&&sa[i]>=(1+len1)&&sa[i]<=len1+len2)
 97                 maxx=height[i];
 98         }
 99     }
100     printf("%d\n",maxx);
101     return 0;
102 }

 

转载于:https://www.cnblogs.com/letlifestop/p/10390384.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值