`

poj 2406

    博客分类:
  • KMP
 
阅读更多

字符串模式匹配通俗点说就是一种在一个字符串中定位另一个串的高效算法。简单匹配算法的时间复杂度为O(m*n);KMP匹配算法。可以证明它的时间复杂度为O(m+n).

 一:让我们先来看基本的简单匹配算法:

  先来看一个简单匹配算法的函数:

int Index_BF ( char S [ ], char T [ ], int pos )
{
/* 若串 S 中从第pos(S 的下标0≤pos<StrLength(S))个字符
起存在和串 T 相同的子串,则称匹配成功,返回第一个
这样的子串在串 S 中的下标,否则返回 -1    */
int i = pos, j = 0;
while ( S[i+j] != '/0'&& T[j] != '/0')
if ( S[i+j] == T[j] )
j ++; // 继续比较后一字符
else
{
i ++; j = 0; // 重新开始新的一轮匹配
}
if ( T[j] == '/0')
return i; // 匹配成功   返回下标
else
return -1; // 串S中(第pos个字符起)不存在和串T相同的子串
}

此算法的思想是直截了当的:将主串S中某个位置i起始的子串和模式串T相比较。即从 j=0 起比较S[i+j]  T[j],若相等,则在主串 S 中存在以 i 为起始位置匹配成功的可能性,继续往后比较( j逐步增1 ),直至与T串中最后一个字符相等为止,否则改从S串的下一个字符起重新开始进行下一轮的"匹配",即将串T向后滑动一位,即 i 1,而 j 退回至0,重新开始新一轮的匹配。



 

当这样一个失配发生时,T下标必须回溯到开始,S下标回溯的长度与T相同,然后S下标增1,然后再次比较。如图:这次立刻发生了失配,T下标又回溯到开始,S下标增1,然后再次比较。如图:


 

 

这次立刻发生了失配,T下标又回溯到开始,S下标增1,然后再次比较。如图:

 



 

 

又一次发生了失配,所以T下标又回溯到开始,S下标增1,然后再次比较。这次T中的所有字符都和S中相应的字符匹配了。函数返回TS中的起始下标3。如图:

 

 

 



 二 KMP算法
还是相同的例子,在S=”abcabcabdabba”中查找T=”abcabd”,如果使用KMP匹配算法,当第一次搜索到S[5] T[5]不等后,S下标不是回溯到1T下标也不是回溯到开始,而是根据TT[5]==’d’的模式函数值(next[5]=2,为什么?后面讲),直接比较S[5] T[2]是否相等,因为相等,ST的下标同时增加;因为又相等,ST的下标又同时增加。。。最终在S中找到了T。如图:



 

KMP匹配算法和简单匹配算法效率比较,一个极端的例子是:

S=“AAAAAA…AAB“(100A)中查找T=”AAAAAAAAAB”, 简单匹配算法每次都是比较到T的结尾,发现字符不同,然后T的下标回溯到开始,S的下标也要回溯相同长度后增1,继续比较。如果使用KMP匹配算法,就不必回溯.

对于一般文稿中串的匹配,简单匹配算法的时间复杂度可降为O (m+n),因此在多数的实际应用场合下被应用。

KMP算法的核心思想是利用已经得到的部分匹配信息来进行后面的匹配过程。看前面的例子。为什么T[5]==’d’的模式函数值等于2next[5]=2),其实这个2表示T[5]==’d’的前面有2个字符和开始的两个字符相同,且T[5]==’d’不等于开始的两个字符之后的第三个字符(T[2]=’c’.如图:



 

 

也就是说,如果开始的两个字符之后的第三个字符也为’d’,那么,尽管T[5]==’d’的前面有2个字符和开始的两个字符相同,T[5]==’d’的模式函数值也不为2,而是为0

   前面我说:在S=”abcabcabdabba”中查找T=”abcabd”,如果使用KMP匹配算法,当第一次搜索到S[5] T[5]不等后,S下标不是回溯到1T下标也不是回溯到开始,而是根据TT[5]==’d’的模式函数值,直接比较S[5] T[2]是否相等。。。为什么可以这样?

刚才我又说:next[5]=2),其实这个2表示T[5]==’d’的前面有2个字符和开始的两个字符相同。请看图 :因为,S[4] ==T[4]S[3] ==T[3],根据next[5]=2,有T[3]==T[0]T[4] ==T[1],所以S[3]==T[0]S[4] ==T[1](两对相当于间接比较过了),因此,接下来比较S[5] T[2]是否相等。。。



 

 

有人可能会问:S[3]T[0]S[4] T[1]是根据next[5]=2间接比较相等,那S[1]T[0]S[2]T[0]之间又是怎么跳过,可以不比较呢?因为S[0]=T[0]S[1]=T[1]S[2]=T[2],而T[0] != T[1], T[1] != T[2],==> S[0] != S[1],S[1] != S[2],所以S[1] != T[0],S[2] != T[0]. 还是从理论上间接比较了。

有人疑问又来了,你分析的是不是特殊轻况啊。

假设S不变,在S中搜索T=“abaabd”呢?答:这种情况,当比较到S[2]T[2]时,发现不等,就去看next[2]的值,next[2]=-1,意思是S[2]已经和T[0] 间接比较过了,不相等,接下来去比较S[3]T[0]吧。

假设S不变,在S中搜索T=“abbabd”呢?答:这种情况当比较到S[2]T[2]时,发现不等,就去看next[2]的值,next[2]=0,意思是S[2]已经和T[2]比较过了,不相等,接下来去比较S[2]T[0]吧。

假设S=”abaabcabdabbaS中搜索T=“abaabd”呢?答:这种情况当比较到S[5]T[5]时,发现不等,就去看next[5]的值,next[5]=2,意思是前面的比较过了,其中,S[5]的前面有两个字符和T的开始两个相等,接下来去比较S[5]T[2]吧。

总之,有了串的next值,一切搞定。那么,怎么求串的模式函数值next[n]呢?(本文中next值、模式函数值、模式值是一个意思。)

 

 

 

意义

 next 函数值究竟是什么含义,前面说过一些,这里总结。

设在字符串S中查找模式串T,若S[m]!=T[n],那么,取T[n]的模式函数值next[n],

1.       next[n]= -1 表示S[m]T[0]间接比较过了,不相等,下一次比较 S[m+1] T[0]

2.       next[n]=0 表示比较过程中产生了不相等,下一次比较 S[m] T[0]

3.       next[n]= k >0 k<n, 表示,S[m]的前k个字符与T中的开始k个字符已经间接比较相等了,下一次比较S[m]T[k]相等吗?

4.       其他值,不可能。

 

怎么样求next[k]代码模板如下:

void get_next()
{
    int j=1, k=0;
    next[0]=-1;
	next[1]=0;
    while(j< lenp)
	{
        if(pat[j] == pat[k])
		{
			next[j+1]=k+1;
			j++;
			k++;
        }
		else if (k==0)
		{
			next[j+1]=0;
			j++;
		}
        else k=next[k];
    }
}

 

 

 

 

 

模式匹配过程是这样:

       当模式串t中的tj与主串中的si比较不相等时,若模式串中存在真子串"t0t1...tk-1"="tj-ktj-k+1...tj-1",此时可以将模式串t按照k=next[j]的值右移,然后比较si与tk(仔细想想),若仍有si!=tk,则继续按照k=next[k]进行迭代,继续右滑,然后比较si与tk。这样的过程一直进行到k=0,此时,若仍有si!=t0,则比较si+1与t0。

next[j]的值有三种意思:

       当串有真子串,为k(0<k<j);

       当串无真子串,为0;

       当j=0,为-1。

 

代码模板如下:

 

int KMP()
{
    int i= 0, j= 0;
    get_next();
    while(i < lent && j < lenp)
	{
        if(j == -1 || text[i] == pat[j])
		{
            i ++; 
			j ++;
        }
        else j = next[j];
    }
    if(j >= lenp) 
		return i - lenp;
    else 
		return -1;
}

 

 KMP算法的模板如下:

#include<iostream>
using namespace std;
const int Max = 10000;

char text[Max], pat[Max];
int lent, lenp;
int next[Max];

void get_next()
{
    int j=1, k=0;
    next[0]=-1;
	next[1]=0;
    while(j< lenp)
	{
        if(pat[j] == pat[k])
		{
			next[j+1]=k+1;
			j++;
			k++;
        }
		else if (k==0)
		{
			next[j+1]=0;
			j++;
		}
        else k=next[k];
    }
}

int KMP()
{
    int i= 0, j= 0;
    get_next();
    while(i < lent && j < lenp)
	{
        if(j == -1 || text[i] == pat[j])
		{
            i ++; 
			j ++;
        }
        else j = next[j];
    }
    if(j >= lenp) 
		return i - lenp;
    else 
		return -1;
}

int main()
{
    scanf("%s%s", text, pat);
    lent = strlen(text);
    lenp = strlen(pat);
    printf("%d\n", KMP());
    return 0;
}

 

 

至于2406: 题意:给出一个串,问你这个最多是多少个相同的字串重复连接而成的。如:ababab则最多有3个ab连接而成。

思路:KMP中的get_next(),或者get_nextval(),对next数组的应用。next[len]是最后一个字符跳的步长,如果他有相同字符串,则该串长度是len-next[len](这点我还在想要怎么证明!)...如果整个长度len能分解成x个这种串(能整除),就得到ans了。否则不能分解。只能是由他自己组成串,长度为1。

代码如下:

#include<iostream>
#include<string>
using namespace std;
const int Max = 100000005;

char str[Max];        //  模式串。
int len, next[Max];

void get_next()
{
    int j=1, k = 0;
    next[0]=-1;
	next[1]=0;
    while(j < len)
	{
        if(str[j] == str[k])
		{
			next[j+1]=k+1;
			j++;
			k++;
        } 
		else if (k==0)
		{
			next[j+1]=0;
			j++;
		}
        else k= next[k];
    }
}

int main()
{
    while(scanf("%s", str) != EOF)
	{
        if(str[0] == '.') break;
        len = strlen(str);
        get_next();
        int ans = 1;
        if(len % (len-next[len]) == 0)       //  看整个长度len能分解成x个这种串。
            ans = len / (len-next[len]);
        printf("%d\n", ans);
    }
    return 0;
}
 

 

 关于KMP算法的理解需要自己慢慢去体会~~

  • 大小: 28.1 KB
  • 大小: 25.4 KB
  • 大小: 25.9 KB
  • 大小: 25.5 KB
  • 大小: 24.5 KB
  • 大小: 11.6 KB
分享到:
评论

相关推荐

    pku poj 2406

    #include using namespace std; #define M 1000000 char t[M+1],p[M+1]; int lent,lenp; bool kmp(char *t,char *p) { int i,j; for(i=lenp,j=0;i;i++) { if(t[i]!=p[j]) return false;...}

    poj题目分类

    * KMP 算法:例如 poj1961、poj2406。 4. 搜索: * 最优化剪枝和可行性剪枝。 * 搜索的技巧和优化:例如 poj3411、poj1724。 * 记忆化搜索:例如 poj3373、poj1691。 5. 动态规划: * 较为复杂的动态规划:...

    POJ 分类题目 txt文件

    例如,题目poj1961和poj2406就是KMP算法的应用实例。 ### 7. 几何算法 几何算法涉及点、线、面的性质和关系,如凸包、最近点对、三角剖分等问题。这些算法在计算机图形学、地理信息系统等领域有着广泛的应用。例如...

    acm训练计划(poj的题)

    - (poj1961, poj2406):高效的字符串匹配算法。 ### 十、进阶状态压缩 1. **状态压缩技巧**: - 如何高效地表示和压缩状态。 2. **状态压缩优化**: - (poj3411, poj1724):进一步提高状态压缩动态规划的效率...

    经典 的POJ 分类

    - POJ 1961、POJ 2406:KMP算法的应用。 ### 其他问题 #### 贪心算法 - **题目示例**: - POJ 3411、POJ 1724:贪心策略的选择与应用。 #### 二分查找 - **题目示例**: - POJ 3373、POJ 1691:二分查找技术的...

    acm新手刷题攻略之poj

    - 推荐题目:[poj1961](https://vjudge.net/problem/POJ-1961)、[poj2406](https://vjudge.net/problem/POJ-2406) - 字符串匹配算法如KMP算法可以提高字符串匹配的效率。 以上列举的题目只是冰山一角,POJ上还有...

    ACM-POJ 算法训练指南

    5. **字符串匹配**:如KMP算法(poj1961, poj2406)。 ### 十三、进阶算法 1. **组合优化**:如割平面法、分支定界法等(poj3411, poj1724)。 2. **近似算法**:解决NP难问题时的近似求解方法(poj3373, poj1691...

    ACMer需要掌握的算法讲解 (2).docx

    * KMP算法:POJ1961、POJ2406 * 左偏树(可合并堆) 四、搜索 * 最优化剪枝和可行性剪枝 * 较为复杂的动态规划:POJ1191、POJ1054、POJ3280、POJ2029、POJ2948、POJ1925、POJ3034 * 记录状态的动态规划:POJ3254、...

    ACM常用算法及其相应的练习题.docx

    ACM常用算法及其相应的练习题 本资源总结了 ACM 竞赛中常用的算法和相应的练习题,涵盖了基础算法、图算法、数据... + poj1961, poj2406 四、搜索 * 最优化剪枝和可行性剪枝 * 搜索的技巧和优化 + poj3411, poj1724

    ACMer需要掌握的算法讲解.docx

    3. KMP 算法(poj1961, poj2406) 4. 左偏树(可合并堆) 四、搜索 1. 最优化剪枝和可行性剪枝 2. 较为复杂的动态规划(如动态规划解特别的施行商问题等) 3. 记录状态的动态规划 4. 树型动态规划 五、计算几何学...

    ACM 题型

    - 示例题目:poj1961, poj2406 #### 高级动态规划 1. **矩阵乘法** - 示例题目:poj1191, poj1054, poj3280, poj2029, poj2948, poj1925, poj3034 2. **四边形不等式** - 示例题目:POJ3254, poj2411, poj1185...

    算法训练方案详解

    - **示例题目**: POJ 3267, POJ 1836, POJ 1260, POJ 2533, POJ 3176, POJ 1080, POJ 1159 - **注意事项**: 理解每种模型的状态定义和转移方程。 #### 六、数学 **1. 组合数学** - **定义**: 研究组合结构的...

    ACM算法总结--最新总结的ACM算法

    6. **KMP算法**(如poj1961, poj2406):高效的字符串匹配算法,适用于文本检索、模式识别等领域。 #### 优化技巧 1. **代码优化**(如poj3096, poj3007):包括循环展开、条件判断优化等,提高代码执行效率。 2. ...

    poj部分源码--Java

    poj平台有关数据结构题的Java源码 1159 1276 2406 2502 2509 2513 2533 2778 3176

    POJ各题算法分类和题目推荐 ACM必看

    * 短代码:1147、1163、1922、2211、2215、2229、2232、2234、2242、2245、2262、2301、2309、2313、2334、2346、2348、2350、2352、2381、2405、2406 * 中短代码:1014、1281、1618、1928、1961、2054、2082、2085...

    poj题目分类...

    * 2406 Power Strings * 2339 Rock, Scissors, Paper * 2350 Above Average * 2218 Does This Make Me Look Fat? * 2260 Error Correction * 2262 Goldbach's Conjecture * 2272 Bullseye 博弈类 博弈类题目是 POJ...

    POJ PKU 必做题+部分难题1001-2500

    1129 1149 1154 1165 1182 1185 1190 1191 1201 1251 1273 1275 1276 1286 1322 1338 1363 1364 1401 1456 1459 1564 1579 1637 1657 1658 1679 ...2104 2112 2115 2186 2255 2352 2369 2406 2409 2421 2479 2480 2498

    acm poj 源代码

    1001 1002 1003 1004 1005 1006 1007 1008 1011 1012 1013 1014 1015 1017 ...2406 2411 2418 2421 2441 2479 2485 2487 2488 2506 2513 2521 2524 2533 2538 2545 2550 2551 2560 2591 2593 2601 2608 2626 2636 2643 ...

    poj pku 解题报告

    1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1011 1012 1013 1014 ...2406 2411 2413 2419 2421 2446 2449 2456 2479 2488 2492 2503 2509 2513 2524 2528 2531 2533 2545 2553 2559 2564 2575 2576 2586 2591 ...

    poj135道题的代码

    1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1011 1012 1013 1014 ...2406 2411 2421 2479 2524 2576 2707 2777 2812 2818 2840 3168 3219 3224 3250 3253 3278 3302 3311 3312 3325 3348 3349 3355 3357 3372 ...

Global site tag (gtag.js) - Google Analytics