\(\color{purple}{Link}\)

\(\text{Solution:}\)

题目要求找到两个串的最长公共子串。\(LCP\)

我们将两个串中间和末尾插入终止符,并弄到一棵后缀树上去。

然后我们发现,对于一个叶子节点,它属于哪个子串,我们只需要找到它的父边上第一个出现的终止符属于哪个边即可。

这里,我们可以用个奇技淫巧——前缀和实现。

介于\(\text{Suffix Tree}\)的边都是压缩的,所以维护信息变得不是很容易,所以可以采用一个在插入外面进行预处理前缀和的方式维护。

然后,我们只需要找到一个最深的非叶子节点,使得它的子树中既含有第一个串串的终止符,也含有第二个串串的终止符即可。

此时它的答案就是这个点的深度。

\(\text{Suffix Tree}\)的主要问题就在于边上信息的维护。如果找不到一个好的方法去维护,\(\text{Suffix Tree}\)还是很麻烦的。

最近做题,题解区域都没有\(\text{Suffix Tree}\)的题解,做起来真的挺累……我太菜了。

  1. #include<bits/stdc++.h>
  2. #include<ctime>
  3. using namespace std;
  4. const int MAXN=1.2e6+10;
  5. string Z,z;
  6. int n,val[MAXN],ans,tot;
  7. int sum[MAXN],sum2[MAXN];
  8. const int inf=(1<<30);
  9. struct SuffixTree {
  10. int link[MAXN],ch[MAXN][28],now,rem,n;
  11. int start[MAXN],len[MAXN],tail,s[MAXN];
  12. SuffixTree() {
  13. tail=now=1;
  14. rem=n=0;
  15. len[0]=inf;
  16. }
  17. inline int build(int a,int b) {
  18. link[++tail]=1;
  19. start[tail]=a;
  20. len[tail]=b;
  21. return tail;
  22. }
  23. void Extend(int x) {
  24. s[++n]=x;
  25. ++rem;
  26. for(int last=1; rem;) {
  27. while(rem>len[ch[now][s[n-rem+1]]])
  28. rem-=len[now=ch[now][s[n-rem+1]]];
  29. int &v=ch[now][s[n-rem+1]];
  30. int c=s[start[v]+rem-1];
  31. if(!v||x==c) {
  32. link[last]=now;
  33. last=now;
  34. if(!v)v=build(n,inf);
  35. else break;
  36. } else {
  37. int u=build(start[v],rem-1);
  38. ch[u][c]=v;
  39. ch[u][x]=build(n,inf);
  40. start[v]+=rem-1;
  41. len[v]-=rem-1;
  42. link[last]=v=u;
  43. last=u;
  44. }
  45. if(now==1)--rem;
  46. else now=link[now];
  47. }
  48. }
  49. } T;
  50. void predfs(int u,int dep) {
  51. if(dep>=inf) {
  52. int L=T.start[u];
  53. int R=L+T.len[u]-1;
  54. R=min(R,T.n);
  55. int V=sum[R]-sum[L-1];
  56. if(V)val[u]=1;
  57. else{
  58. V=sum2[R]-sum2[L-1];
  59. if(V)val[u]=2;
  60. }
  61. return;
  62. }
  63. for(int i=0; i<28; ++i) {
  64. if(T.ch[u][i]) {
  65. predfs(T.ch[u][i],dep+T.len[T.ch[u][i]]);
  66. val[u]|=val[T.ch[u][i]];
  67. }
  68. }
  69. if(val[u]>=3)ans=max(ans,dep);
  70. }
  71. char buf[1<<21],*p1=buf,*p2=buf;
  72. string read(){
  73. #define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
  74. string s="";
  75. char ch=gc();
  76. while(ch=='\n')ch=gc();
  77. while(ch!='\n')s+=ch,ch=gc();
  78. return s;
  79. }
  80. int main() {
  81. // freopen("1.in","r",stdin);
  82. // freopen("SP.out","w",stdout);
  83. clock_t ST,ET;
  84. ST=clock();
  85. z=read();Z=read();
  86. z+=(char)'a'+26;
  87. z+=Z;z+=(char)'a'+27 ;
  88. for(int i=0;i<z.size();++i)z[i]-='a',T.Extend(z[i]);
  89. tot=z.size();
  90. for(int i=1; i<=tot; ++i) {
  91. sum[i]=sum[i-1]+(T.s[i]==26);
  92. sum2[i]=sum2[i-1]+(T.s[i]==27);
  93. }
  94. predfs(1,0);
  95. printf("%d\n",ans);
  96. ET=clock();
  97. // cout<<(double)(ET-ST)/CLOCKS_PER_SEC<<"s"<<endl;
  98. return 0;
  99. }

注意,如果在\(dfs\)里面来根据这条边的起点和终点暴力处理的话,这就是个\(n^2\)暴力。观察到,我们只需要在叶子的节点处理,而在叶子节点暴力处理的复杂度也不够优秀。

观察到,第一个字符串的终止符一定先于第二个字符串的终止符出现(如果有的话)。那么,根据前缀和,先判断第一个终止符,再判断第二个终止符即可。

最后时间复杂度是:

\(\text{We let D show the constant,then the complexity is O(D*N).N is the length of these strings.}\)

【题解】SP1811 LCS - Longest Common Substring的更多相关文章

  1. SP1811 LCS - Longest Common Substring

    \(\color{#0066ff}{ 题目描述 }\) 输入2 个长度不大于250000的字符串,输出这2 个字符串的最长公共子串.如果没有公共子串则输出0 . \(\color{#0066ff}{输 ...

  2. 「双串最长公共子串」SP1811 LCS - Longest Common Substring

    知识点: SAM,SA,单调栈,Hash 原题面 Luogu 来自 poj 的双倍经验 简述 给定两字符串 \(S_1, S_2\),求它们的最长公共子串长度. \(|S_1|,|S_2|\le 2. ...

  3. 【SP1811】LCS - Longest Common Substring

    [SP1811]LCS - Longest Common Substring 题面 洛谷 题解 建好后缀自动机后从初始状态沿着现在的边匹配, 如果失配则跳它的后缀链接,因为你跳后缀链接到达的\(End ...

  4. 后缀自动机(SAM) :SPOJ LCS - Longest Common Substring

    LCS - Longest Common Substring no tags  A string is finite sequence of characters over a non-empty f ...

  5. spoj1811 LCS - Longest Common Substring

    地址:http://www.spoj.com/problems/LCS/ 题面: LCS - Longest Common Substring no tags  A string is finite ...

  6. spoj 1811 LCS - Longest Common Substring (后缀自己主动机)

    spoj 1811 LCS - Longest Common Substring 题意: 给出两个串S, T, 求最长公共子串. 限制: |S|, |T| <= 1e5 思路: dp O(n^2 ...

  7. LCS - Longest Common Substring(spoj1811) (sam(后缀自动机)+LCS)

    A string is finite sequence of characters over a non-empty finite set \(\sum\). In this problem, \(\ ...

  8. SPOJ 1811 LCS - Longest Common Substring

    思路 和SPOJ 1812 LCS2 - Longest Common Substring II一个思路,改成两个串就有双倍经验了 代码 #include <cstdio> #includ ...

  9. SPOJ1811 LCS - Longest Common Substring(后缀自动机)

    A string is finite sequence of characters over a non-empty finite set Σ. In this problem, Σ is the s ...

随机推荐

  1. Unity Prefab关联

    Unity3D研究院之Prefab里面的Prefab关联问题http://www.xuanyusong.com/archives/3042

  2. linux的五种IO模型

    概念: 同步.异步.阻塞.非阻塞的概念 同步:所谓同步,发起一个功能调用的时候,在没有得到结果之前,该调用不返回,也就是必须一件事一件事的做,等前一件做完了,才能做下一件. 提交请求->等待服务 ...

  3. Vue官方文档Vue.extend、Vue.component、createElement、$attrs/$listeners、插槽的深入理解

    一.Vue.extend({}). 看官网文档介绍,Vue.extend({})返回一个Vue的子类,那么这个Vue子类是啥玩意儿呢?我直观感觉它就是创建出一个组件而已啊,那么它又和Vue.compo ...

  4. 线上环境去除console

    npm i -D babel-plugin-transform-remove-console babel.config.js // 获取 VUE_APP_ENV 非 NODE_ENV,测试环境依然 c ...

  5. 0vscode基本插件

    Bracket Pair Colorizer auto-close-tag Auto Rename Tag Bracket Pair Colorizer Dracula ESLint  Code Sp ...

  6. ArrayList源码剖析与代码实测

    ArrayList源码剖析与代码实测(基于OpenJdk14) 目录 ArrayList源码剖析与代码实测(基于OpenJdk14) 继承关系 从构造函数开始 从add方法深入 / 数组的扩容 其他的 ...

  7. Apache和分布式部署

    1.tomcat分布式部署 1.1.要配置几个tomcat,就部署几个相同程序名的tomcat 1.2.配置每个tomcat下server.xml中ajp端口,以及后面的jvmRoute,第几个就配置 ...

  8. end的用法——print中加end=可以不换行展示

    A=['hello','world',1,2,3]for i in A: print('正常输出i的值:',i) #打印出来的是换行展示hello world 1 2 3 print('加入sep后i ...

  9. [POJ3253]Fence Repair(单调队列)

    题目链接 http://poj.org/problem?id=3253 题目描述 大意:切长度为a的木条的花费是a,给定最终切好的n段各自的长度,问由原来的一根木条(长度为n段长度和)以最终总花费最小 ...

  10. Dubbo必须会的知识点

    前言 应用架构演变: 单一架构ORM:单机构建网站,是一个高内聚版本,所有功能部署在一起.通过一个容器和JSP/Servlet技术或通过一些开源的框架如SSM以及SSH,通过数据库管理系统来存储数据. ...