KMP能计算一个字符串的每个位置前最长公共前缀后缀

扩展KMP可以用来计算两个字符串间的最长公共前缀后缀的……

不过为了计算这个需要绕些弯路

已知字符串$S$和$P$,$S$的长度为$n$,$P$的长度为$m$

扩展KMP实际是计算$E$数组,设$E[i]$为字符串$S[i..n-1]$与字符串$P$的最大公共前缀

尝试数学归纳法

$E[0]$显然只能直接依次比对,因为什么信息都没有

假设$E[0]\sim E[i-1]$都计算出来了,现在计算$E[i]$

不妨= =,利用$E[i-1]$的信息,第二行的矩形的宽度表示$E[i-1]$,只有这个信息是不够的,否则计算$E[i]$还是需要重复跑计算$E[i-1]$跑过的距离

假设有“$P[i..m-1]$与$P$的最大公共前缀长度”的信息,设为$N[i]$

  • 如果$i\geqslant (i-1)+E[i-1]$,说明上个信息对这个没帮助,直接往右跑
  • 如果$i+N[1]\geqslant (i-1)+E[i-1]$,由于红线右边的P与S是否相等不确定,因此要舍去红线右边的部分,那么经过如图的变形(第三排和第四排的矩形),可以直接从上一次失败的地方继续(红线处)
  • 如果$i+N[1]<(i-1)+E[i-1]$,因为在红线前就失败了,那么直接就可以得到$E[i]=N[1]$

由于第一种情况中仍然可能重复对比$S$和$P$,$E[i-1]$可能不是最好的选择,那么我们就选红线最靠右的$E[k]$来计算$E[i]$

  • 如果$i\geqslant j$,说明上个信息对这个没帮助,直接往右跑
  • 如果$i+N[i-k]\geqslant j$,由于红线右边的P与S是否相等不确定,因此要舍去红线右边的部分,那么经过如图的变形(第三排和第四排的矩形),可以直接从上一次失败的地方继续(红线处)
  • 如果$i+N[i-k]< j$,因为在红线前就失败了,那么直接就可以得到$E[i]=N[i]$

这样,在知道$N[i]$的情况下,可以$\mathcal{O}(n)$得到$E$数组(因为S与P的比较不会重复),前两种情况可以合并为一个,并且可以省去单独计算$E[0]$

代码:

inline void getE() {
int k = 0, j=0;
REP(i,0,n) {
if( i>=j || i+N[i-k] >= j ) {
if( i>=j ) j=i;
while( j < n && j - i < m && s[j] == p[j-i]) j++;
E[i] = j-i;
k = i;
} else {
E[i] = N[i-k];
}
}
}

对于$N$数组,和求$E$数组类似

$N[0]=m$,$N[1]$直接计算,假设$N[0]\sim N[i-1]$都求出来了,选红线最靠右的$N[k]$,那么

  • 如果$i\geqslant j$,说明上个信息对这个没帮助,直接往右跑
  • 如果$i+N[i-k]\geqslant j$,那么直接从上一次失败的地方继续(红线处)
  • 如果$i+N[i-k]< j$,因为在红线前就失败了,那么$N[i]=N[i-k]$

代码:

inline void getN() {
int k = 0, j=0;
N[0] = m;
REP(i,1,m) {
if( i>=j || i+N[i-k] >= j ) {
if( i>=j ) j=i;
while( j < m && p[j] == p[j-i]) j++;
N[i] = j-i;
k = i;
} else {
N[i] = N[i-k];
}
}
}

HDU-2594

题目

给两个字符串,第一个字符串与第二个字符串的最长公共前缀后缀

题解

直接套用EXKMP第一个字符串设为p,第二个字符串设为s,找到第一个i,使E[i]=n-i,就可以了

AC代码

#include<cstdio>
#include<cstring>
#include<cassert>
#ifdef sahdsg
#define DBG(...) printf(__VA_ARGS__),fflush(stdout)
#else
#define DBG(...) (void)0
#endif // sahdsg
using namespace std;
#define REP(r,x,y) for(register int r=(x); r<(y); r++)
#define MAXN 50007
char p[MAXN], s[MAXN];
int N[MAXN], m;
int E[MAXN], n; inline void getN() {
int k = 0, j=0;
N[0] = m;
REP(i,1,m) {
if( i>=j || i+N[i-k] >= j ) {
if( i>=j ) j=i;
while( j < m && p[j] == p[j-i]) j++;
N[i] = j-i;
k = i;
} else {
N[i] = N[i-k];
}
}
} inline void getE() {
int k = 0, j=0;
REP(i,0,n) {
if( i>=j || i+N[i-k] >= j ) {
if( i>=j ) j=i;
while( j < n && j - i < m && s[j] == p[j-i]) j++;
E[i] = j-i;
k = i;
} else {
E[i] = N[i-k];
}
}
} int main() {
#ifdef sahdsg
freopen("in.txt", "r", stdin);
#endif // sahdsg
while(~scanf("%s%s", p,s)) {
n=strlen(s),m=strlen(p);
getN();
getE();
int t=-1;
// REP(i,0,n) DBG("%d ", E[i]);
REP(i,0,n) {
if(E[i]==n-i) {
t=i;
break;
}
}
if(~t) {
printf("%s %d\n", s+t, n-t);
} else puts("0");
} return 0;
}

最新文章

  1. perl 删除过期文件
  2. 《CLR.via.C#第三版》第一部分读书笔记(一)
  3. 转!!EL表达式大全
  4. android源码编译1
  5. Java程序员面试题集(1-50)(转)
  6. Map.containsKey方法——判断Map集合对象中是否包含指定的键名
  7. java 三大框架
  8. 理解滑动平均(exponential moving average)
  9. HTTP 请求头中的 Remote_Addr,X-Forwarded-For,X-Real-IP
  10. 一窍懂PID
  11. Codeforces Round #539 div2
  12. 【算法和数据结构】_16_小算法_IntToStr: 将整型数据转换为字符串
  13. 转://Linux下tmpfs介绍及使用
  14. centos下配置sftp且限制用户访问目录
  15. ElasticSearh更新nested字段(Array数组)。怎么根据查询条件(query)复制一个(index)到新的Index how to update by query a nested fields data for elasticsearch
  16. jzoj3363
  17. mac下为什么光标按方向键只能一个字一个字地蹦
  18. python循环删除列表里的元素!漏删!
  19. ScheduledThreadPoolExecutor的scheduleAtFixedRate方法探究
  20. perl I/O和缓存的关系

热门文章

  1. 1、netty入门说明
  2. SQL Server如何正确的删除Windows认证用户
  3. XTTS Creates Alias on Destination when Source and Destination use ASM (Doc ID 2351123.1)
  4. &lt;String&gt; 161 358
  5. JS Foo.getName笔试题解析,杂谈静态属性与实例属性,变量提升,this指向,new一个函数的过程
  6. 在Python中使用MySQL--PyMySQL的基本使用
  7. redis 事务(悲观锁和乐观锁)
  8. 母版页 treeview控件 SiteMapPath控件 treeview数据库绑定模式
  9. socket经典案例-发送数据
  10. 【Untiy】完美解决Untiy Package Manager无限加载的问题