数据结构之字符串的模式匹配
2015-08-25 21:49
399 查看
字符串的模式匹配问题:
一共有两种算法,
1.朴素模式匹配算法。
举例而言(寻找从S=”goodgoogle”中找到V=”google”这个子串):我们一般需要以下的步骤
(1).从主串的第一个字符开始,S与V中的字符逐一比较,可以发现前三个匹配成功而第四个没有匹配成功(竖线代表成功,折现代表失败)。
(2)主串的开始下表+1,又继续与子串进行匹配
(3)如果没有匹配成功那么继续重复第二步,直到全部匹配成功,或者直到s到达末尾匹配失败。
实现代码:
//返回子串T在主串S中第pos个字符之后的位置。若不存在,则函数返回值为0.
//T非空,1<= pos <=s.length
Public int ndex(String s,String v,int pos){
int i = pos-1; // i用于主串s中当前位置下标,若pos不为0,则从post的位置开始
int j = 0; // j用于子串v的当前位置下标。
while(i<s.length && j<v.length){ //若i小于s的长度,且j小于v的长度时循环
if(s[i] == v[j]){
//如果当前主串对应字符和子串对应字符相等
i++;
J++
}else{
//说明在匹配某个过程中,当前s的对应字符与v对应字符不相等。
i = i - j + 1; //退回到上次匹配开始的下一个字符
j = 0; //将J退回到子串的开头
}
}
if (j > v.length-1){
return i - v.length + 1;
}else {
return 0;
}
}
对于上面的算法,我提出一些改进意见就是,匹配的终止条件可以具体一些,减少循环的次数。当主串的剩余子串长度小于要寻找的子串长度时,就可以停止匹配了。
可以在while中比较两个字符串相等位置的代码之前,加一个大的判断if(s.length-1 - i < v.length ),如果满足条件直接break,跳出循环,后面的字符可以不用比较。
2.KMP模式匹配算法(是对1算法的改进)。
假如有主串 S= “abcdefgab”; 匹配子串:T=”abcdex”;我们使用上面的朴素匹配法过程如下。
如果我们认真观察其实可以发现②③④⑤步骤是不需要的,是重复的无用操作。因为我们可以发现对于子串的首字符a并不与后面任意一个字符相等,并且如果出现①中的这种现象,子串的前5个字符与主串相等,那么就意味着子串T不可能在于主串S中的2-5号字符相等,2-5号操作是多余的。这就是KMP算法改进朴素算法的地方。
KMP模式匹配算法的核心是:如果我们知道子串T的首字符与T中后面的字符均不相等(这是前提),如果在出现如①的情况,即子串的前部分与主串的前部分字符相等,但是从i号不相等,那么我们可以断定以2 到 i-1号任意一个字符开头的字符串不可能与子串匹配成功,那么主串的2 到 i-1号不需要进行判断,直接从i号开始判断。
如果子串中有与首字符重复的子符,该怎么办?我们以S=”abcabcabc” T=”abcabx”为例说明。首先继续朴素匹配算法。
因为首字符a有重复,我们不能继续运用上面的规则,但是我们可以发现,到第二个出现首字符之前的字符我们是可以使用规则,将其规避掉,即②与③去掉,不要判断。
因此:对于子串中有与首字符相等的字符,可以省略掉一部分不需要的判断。
这里如何判断与首字符相同的不同位置字符,对算法的影响,参考《大话数据结构》P139.
一共有两种算法,
1.朴素模式匹配算法。
举例而言(寻找从S=”goodgoogle”中找到V=”google”这个子串):我们一般需要以下的步骤
(1).从主串的第一个字符开始,S与V中的字符逐一比较,可以发现前三个匹配成功而第四个没有匹配成功(竖线代表成功,折现代表失败)。
(2)主串的开始下表+1,又继续与子串进行匹配
(3)如果没有匹配成功那么继续重复第二步,直到全部匹配成功,或者直到s到达末尾匹配失败。
实现代码:
//返回子串T在主串S中第pos个字符之后的位置。若不存在,则函数返回值为0.
//T非空,1<= pos <=s.length
Public int ndex(String s,String v,int pos){
int i = pos-1; // i用于主串s中当前位置下标,若pos不为0,则从post的位置开始
int j = 0; // j用于子串v的当前位置下标。
while(i<s.length && j<v.length){ //若i小于s的长度,且j小于v的长度时循环
if(s[i] == v[j]){
//如果当前主串对应字符和子串对应字符相等
i++;
J++
}else{
//说明在匹配某个过程中,当前s的对应字符与v对应字符不相等。
i = i - j + 1; //退回到上次匹配开始的下一个字符
j = 0; //将J退回到子串的开头
}
}
if (j > v.length-1){
return i - v.length + 1;
}else {
return 0;
}
}
对于上面的算法,我提出一些改进意见就是,匹配的终止条件可以具体一些,减少循环的次数。当主串的剩余子串长度小于要寻找的子串长度时,就可以停止匹配了。
可以在while中比较两个字符串相等位置的代码之前,加一个大的判断if(s.length-1 - i < v.length ),如果满足条件直接break,跳出循环,后面的字符可以不用比较。
2.KMP模式匹配算法(是对1算法的改进)。
假如有主串 S= “abcdefgab”; 匹配子串:T=”abcdex”;我们使用上面的朴素匹配法过程如下。
如果我们认真观察其实可以发现②③④⑤步骤是不需要的,是重复的无用操作。因为我们可以发现对于子串的首字符a并不与后面任意一个字符相等,并且如果出现①中的这种现象,子串的前5个字符与主串相等,那么就意味着子串T不可能在于主串S中的2-5号字符相等,2-5号操作是多余的。这就是KMP算法改进朴素算法的地方。
KMP模式匹配算法的核心是:如果我们知道子串T的首字符与T中后面的字符均不相等(这是前提),如果在出现如①的情况,即子串的前部分与主串的前部分字符相等,但是从i号不相等,那么我们可以断定以2 到 i-1号任意一个字符开头的字符串不可能与子串匹配成功,那么主串的2 到 i-1号不需要进行判断,直接从i号开始判断。
如果子串中有与首字符重复的子符,该怎么办?我们以S=”abcabcabc” T=”abcabx”为例说明。首先继续朴素匹配算法。
因为首字符a有重复,我们不能继续运用上面的规则,但是我们可以发现,到第二个出现首字符之前的字符我们是可以使用规则,将其规避掉,即②与③去掉,不要判断。
因此:对于子串中有与首字符相等的字符,可以省略掉一部分不需要的判断。
这里如何判断与首字符相同的不同位置字符,对算法的影响,参考《大话数据结构》P139.
相关文章推荐
- 动易2006序列号破解算法公布
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- Lua教程(七):数据结构详解
- 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解
- 超大数据量存储常用数据库分表分库算法总结
- C#数据结构与算法揭秘二
- C#冒泡法排序算法实例分析
- C#数据结构揭秘一
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- C#实现的算24点游戏算法实例分析
- c语言实现的带通配符匹配算法
- 数据结构之Treap详解
- 浅析STL中的常用算法
- 算法之排列算法与组合算法详解
- C++实现一维向量旋转算法
- Ruby实现的合并排序算法
- C#折半插入排序算法实现方法
- 基于C++实现的各种内部排序算法汇总