最近做完了kuangbin的一套关于kmp的题目(除了一道字典树的不会,因为还没学字典树所以先放放),做个总结。(kuangbin题目的链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/A

  说实话,kmp这个东西真的理解了很久,因为网上模板的下标都不一样,而且讲解的部分和代码实现又有点差别。话说当初看到一个比较好的博客忘记保存了。。= =

  首先给个最简单的题目:http://oj.acm.zstu.edu.cn/JudgeOnline/problem.php?id=4194

  这就是直接赤裸裸的kmp,这里直接给出代码当作模板:

 #include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std; char a[+],b[+];
int next[+],lena,lenb;
void getnext()
{
next[]=;
int j=;
for(int i=;i<=lenb;i++)
{
while(j>&&b[j+]!=b[i]) j=next[j];
if(b[j+]==b[i]) j++;
next[i]=j;
}
}
int kmp()
{
int cnt=,j=;
for(int i=;i<=lena;i++)
{
while(j>&&b[j+]!=a[i]) j=next[j];
if(b[j+]==a[i]) j++;
if(j==lenb) cnt++,j=;
}
return cnt;
}
int main()
{
while(scanf("%s%s",a+,b+)==)
{
lena = strlen(a+);
lenb = strlen(b+);
getnext();
printf("%d\n",kmp());
}
return ;
}

  这是kmp最基本的应用,在这里,子串不能重复,即aaa中aa只出现一次,如果要aaa中不同起点但是子串有重复部分的也算的话,很简单,只要把kmp中的j=0改成j=nxt[j]即可(只要理解了kmp这还是比较容易想通的)。

  

  接着,就是关于kmp求循环节了,对于求循环节的话,其循环节等于len-nxt[len],这个比较巧妙,自己动手画画图还是可以想通的。

  例题的话推荐kuangbin的D题(HDU3746)。

  

  然后,还可以在中间加个'#'再把原串的反过来的形式接在后边,这样,再使用kmp就可以求得最长回文前缀了,同理,两边都倒过来就可以求得最长回文后缀。加个'#'可以保证新串的公共前缀一定在'#'前面,即回文前缀在'#'前面(巧妙啊!!)。

  题目就是kuangbin的S题(HDU3613)。

  题意就是一个串将其截断,任意一边如果是回文就给出其价值,否则价值为0。要注意的一个地方是,每个字符的价值可能是0,所以ans应当初始化为负无穷以免出错(不过好像本来不是回文就是0所以不需要?我也搞不清楚了,反正保险点没错的。。)。

  AC代码如下:

 #include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std; int value[];
char s[*+];
int nxt[*+];
int pre[*+],pos[*+];
int sum[+];
int len;
void getnxt()
{
int j=;
memset(nxt,,sizeof(nxt));
for(int i=;i<=len;i++)
{
while(j>&&s[j+]!=s[i]) j=nxt[j];
if(s[j+]==s[i]) j++;
nxt[i]=j;
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(sum,,sizeof(sum));
memset(pre,,sizeof(pre));
memset(pos,,sizeof(pos));
for(int i=;i<=;i++) scanf("%d",&value[i]);
scanf("%s",s+); int k=strlen(s+); //k是原串的长度
for(int i=;i<=k;i++) sum[i]=sum[i-]+value[s[i]-'a'+]; //sum[i]表示到i为止的和 len=k;
int sit=k+;
s[k+]='#';
while(len>=) s[++sit]=s[len--]; //把字符串变为以#为中心的对称字符串
s[sit+]=; len=sit; //len是变化后串的总长度
getnxt(); while(nxt[sit]) pre[sit]=T+,sit=nxt[sit]; //表示sit处及以前是回文
pre[]=T+; //第一个字母一定是回文的 for(int i=;i<=k;i++)
{
swap(s[i],s[i+k+]);
}
getnxt();
sit=len;
while(nxt[sit]) pos[sit]=T+,sit=nxt[sit]; //sit是反转以后原串的sit位置及以前是回文
pos[]=T+; //第一个字母一定是回文的 int num=;
int ans=-0x3f3f3f3f; for(int i=;i<k;i++) //在i的后面的位置切断
{
num=;
if(pre[i]) num+=sum[i];
if(pos[k-i]) num+=sum[k]-sum[i];
ans=max(ans,num);
}
printf("%d\n",ans);
}
return ;
}

  扩展kmp说实话我也不会- -,然后要说的就是kmp有个类似的库函数strstr(a,b),判断b是不是a的字串,不是的话返回0,否则返回第一次出现的首地址,如果只要判断有没有出现而不需要知道几次的话这个还是比较方便的。。

  另外,再讲一个最大最小表示法。即关于一个字符串不断的将第一个字符拿到最后,这出现的所有情况中总有一个字典序最大一个字典序最小的,这就是最大最小表示法。也是通过一个例题给出模板,但是我还不能完全证明,只能通过动手画图大概判断它的正确性,还是比较巧妙的,我也没有找出反例(怎么可能找出反例- 。-)。话说字符串的东西都是比较巧妙的。。

  例题是kuangbin的O题(HDU3374)。

  下面给出AC代码:

 #include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std; char s[+];
int nxt[+];
int len;
void getnxt()
{
int j=;
nxt[]=;
for(int i=;i<=len;i++)
{
if(j>&&s[j+]!=s[i]) j=nxt[j];
if(s[j+]==s[i]) j++;
nxt[i]=j;
}
}
int min_max_press(int flag)
{
int i=,j=,k=;
while(i<=len&&j<=len&&k<=len)
{
int sit1,sit2;
sit1=(i+k)%len==?len:(i+k)%len;
sit2=(j+k)%len==?len:(j+k)%len;
int t=s[sit1]-s[sit2];
if(t==) k++;
else
{
if(flag)
{
if(t>) j+=k+;
else i+=k+;
}
else
{
if(t<) j+=k+;
else i+=k+;
}
if(i==j) j++;
k=;
}
}
return min(i,j);
}
int main()
{
int n;
while(scanf("%s",s+)==)
{
len = strlen(s+);
memset(nxt,,sizeof(nxt));
getnxt();
int looplen = len-nxt[len];
int ans=len%looplen?:len/looplen;
int minn=min_max_press();
int maxx=min_max_press();
printf("%d %d %d %d\n",minn,ans,maxx,ans);
}
return ;
}

  最后要讲的是manacher(马拉车)算法。这个算法主要就是用来求一个串中的最长回文长度(当然不限于字符串)。主要思想就是在每两个元素以及开头结尾都插上一个不可能出现的字符如'#',然后对于每一个位置都求出p[i],即新串的每一个点的最大回文长度,然后对于原串来说,这个值减1就是了。具体的也不证明了(其实是我说不清楚- -),和上面的构造得出最大回文前缀有点类似。

  下面给出例题:

  X题(HDU3068):简单暴力地套模板就好了。

  AC代码如下:

 #include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std; char s[+],str[*+];
int p[*+];
int main()
{
while(scanf("%s",s+)==)
{
int sit=;
int len=strlen(s+);
for(int i=;;i++)
{
if(i&)
{
str[i]='#';
}
else
{
str[i]=s[sit++];
}
if(sit==len+) {str[i+]='#';len=i+;break;}
}
int mx=,index=;
str[]='$';
for(int i=;i<=len;i++)
{
if(mx>i)
{
p[i]=min(p[*index-i],mx-i);
}
else p[i]=;
for(;str[i+p[i]]==str[i-p[i]];p[i]++);
if(mx<i+p[i])
{
mx=i+p[i];
index=i;
}
}
int ans=;
for(int i=;i<=len;i++) ans=max(p[i],ans);
printf("%d\n",ans-);
}
return ;
}

  当然对于不是字符串的也可以用这个算法的,比方说V题(HDU4513)。

  

  这套题目里面还有许多好题,但是我在这里只再讲一题,觉得比较复杂的一题。

  L题(HDU4300)。题意是,每次给出两个串,a串是有26个的长度,每个位置表示原来字母表的那个位置转化为密码后的字母。而b串,可以表示成,密码串+原串的形式。密码串是原串通过表a翻译成密码串的。密码串一定完整,而原串可能缺损,或完整或全部没有。然后要你输出全部的完整两个串。举个例子abcde经过翻译以后是qwert。那么如果串b给的是qwertabc,显然后面完整的是abcde所以输出qwertabcde。然后我的思路就是,因为串b中原串的长度一定不会超过串b长度的一半,那么从b中的后一半截断。同时给串b按表翻译成原来的样子。比方说上面这个例子,截断的后半是tabc,然后b串翻译回去是abcdexxx(后面3个不重要了)然后把截断的tabc和abcdexxx的前缀匹配一下,得到tabc的后缀能和他匹配到的最大长度匹配出来就好了,匹配出来是abc嘛,然后在接着输出de就好了。

  思路大致如此,具体见代码:

 #include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std; int nxt[+];
char s1[+],s2[+];
void getnxt(char *s)
{
int j=;
nxt[]=;
int len = strlen(s+);
for(int i=;i<=len;i++)
{
while(j>&&s[j+]!=s[i]) j=nxt[j];
if(s[j+]==s[i]) j++;
nxt[i]=j;
}
}
int kmp(char *s,char *t)
{
memset(nxt,,sizeof(nxt));
int lent=strlen(t+),lens=strlen(s+);
getnxt(t);
int i=,j=;
for(int i=;i<=lens;i++)
{
while(j>&&t[j+]!=s[i]) j=nxt[j];
if(t[j+]==s[i]) j++;
}
return j;
} int main()
{
int T;
scanf("%d",&T);
while(T--)
{
char str[];
scanf("%s%s",str+,s1+);
int len=strlen(s1+);
strcpy(s2+,s1++(len+)/);
printf("%s",s1+);
for(int i=;i<=len;i++)
{
for(int j=;j<=;j++)
{
if(s1[i]==str[j])
{
s1[i]='a'+(j-);
break;
}
}
}
int flag = kmp(s2,s1);
for(int i=flag+;i<=len-flag;i++) printf("%c",s1[i]);
puts("");
}
}

  要注意红色的部分,因为这里WA了好多次QAQ。。

  那么,到这里kmp的就总结完了,还有什么以后想起来再补上好了。。

ACM之路(12)—— KMP & 扩展KMP & Manacher的更多相关文章

  1. 字符串匹配—KMP 扩展KMP Manacher

    kuangbin字符串专题传送门--http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#overview 算法模板: KMP: ; ...

  2. Kuangbin 带你飞 KMP扩展KMP Manacher

    首先是几份模版 KMP void kmp_pre(char x[],int m,int fail[]) { int i,j; j = fail[] = -; i = ; while (i < m ...

  3. 【string】KMP, 扩展KMP,trie,SA,ACAM,SAM,最小表示法

    [KMP] 学习KMP,我们先要知道KMP是干什么的. KMP?KMPLAYER?看**? 正如AC自动机,KMP为什么要叫KMP是因为它是由三个人共同研究得到的- .- 啊跑题了. KMP就是给出一 ...

  4. kmp&扩展kmp

    kmp: KMP的主要目的是求B是不是A的子串,以及若是,B在A中所有出现的位置 写的很详细的大佬的博客:http://www.matrix67.com/blog/archives/115 模板: / ...

  5. kuangbin专题十六 KMP&&扩展KMP HDU2609 How many (最小字符串表示法)

    Give you n ( n < 10000) necklaces ,the length of necklace will not large than 100,tell me How man ...

  6. hdu 4300 Clairewd’s message(kmp/扩展kmp)

    题意:真难懂.. 给出26个英文字母的加密表,明文中的'a'会转为加密表中的第一个字母,'b'转为第二个,...依次类推. 然后第二行是一个字符串(str1),形式是密文+明文,其中密文一定完整,而明 ...

  7. [kuangbin带你飞]专题十六 KMP & 扩展KMP & Manacher 题解报告

    来刷kuangbin字符串了,字符串处理在ACM中是很重要的,一般比赛都会都1——2道有关字符串处理的题目,而且不会很难的那种,大多数时候都是用到一些KMP的性质或者找规律. 点击标题可跳转至VJ比赛 ...

  8. kuangbin专题十六 KMP&&扩展KMP HDU3613 Best Reward(前缀和+manacher or ekmp)

    After an uphill battle, General Li won a great victory. Now the head of state decide to reward him w ...

  9. kuangbin专题十六 KMP&&扩展KMP HDU2328 Corporate Identity

    Beside other services, ACM helps companies to clearly state their “corporate identity”, which includ ...

随机推荐

  1. Map集合中key不存在时使用toString()方法、valueOf()方法和强制转换((String))之间的区别

    1.toString()方法 底层代码 public String toString() { return this; } 其返回值为String类型的字符串本身 Map<String, Obj ...

  2. js写guess网页

    (一)布局           猜前                            ->                         猜后 (二)明确实现功能和具体实现: 1.网页生 ...

  3. 【Hibernate】事务处理

    一.概述 一.概述 事务 事务就是逻辑上的一组操作,要么全都成功,要么全都失败!!! 事务特性 原子性:事务一组操作不可分割. 一致性:事务的执行前后,数据完整性要保持一致. 隔离性:一个事务在执行的 ...

  4. Image Processing and Analysis_21_Scale Space:Edge Detection and Ridge Detection with Automatic Scale Selection——1998

    此主要讨论图像处理与分析.虽然计算机视觉部分的有些内容比如特 征提取等也可以归结到图像分析中来,但鉴于它们与计算机视觉的紧密联系,以 及它们的出处,没有把它们纳入到图像处理与分析中来.同样,这里面也有 ...

  5. [Selenium3+python3.6]自动化测试1-安装

    参考文档: http://www.cnblogs.com/yoyoketang/p/6123890.html 安装环境: win7+Py3.6 +FF45 (ESR version) +seleniu ...

  6. 1.Hbase简介

    1. Hbase简介 1.1. 什么是hbase(面向列) HBASE是一个高可靠性.高性能.面向列.可伸缩的分布式存储系统,利用HBASE技术可在廉价PC Server上搭建起大规模 结构化存储集群 ...

  7. conda create 报错解决

    1. 输入命令: conda create -n query-scorer-serving python=2.7 报错: Solving environment: failed CondaError: ...

  8. Java与CC++交互JNI编程

    哈哈,经过了前面几个超级枯燥的C.C++两语言的基础巩固之后,终于来了到JNI程序的编写了,还是挺不容易的,所以还得再接再厉,戒骄戒躁,继续前行!! 第一个JNI程序: JNI是一种本地编程接口.它允 ...

  9. 2018年5月20日--西安icpc邀请赛打铁总结

    2018年5月20日--西安icpc邀请赛打铁总结  事后诸葛亮 大致回顾一下比赛,29号的热身赛和30号的正式赛. 热身赛总共三道题,一个小时,没有AC一道题目. A题是一个几何题目,审题时犯了一个 ...

  10. CentOS7安装Ambari2.7.4过程【离线安装】

    先配置免密码登录 修改所有结点的host 192.168.210.133 node1 192.168.210.134 node2 192.168.210.135 node3 192.168.210.1 ...