字符串篇
来源:互联网 发布:拍照软件电脑版 编辑:程序博客网 时间:2024/06/02 05:31
<<------表示方法------>>
1. 定长表示方法
#define MAXSTRLEN 255
typedef unsigned char SString[MAXSTRLEN + 1]; //0号单元存放串的长度
2.堆分配表示方法
typedef struct
{
char *ch; //基址
int length; //串长
}HString;
<<------常用算法------>>
1. 模式匹配算法(KMP算法)
子串的定位问题通常称为模式匹配。
一般算法:
int Index(SString S, SString T, int pos)
{ //返回子串T在主串S中的第pos个字符之后的位置。若不存在,则返回。
//其中T非空,<=pos<=StrLength(S).
i = pos; j = 1;
while(i <= S[0] && j <= T[0])
{
if(S[i] == T[j])
{ ++i; ++j; } //继续比较后续字符
else
{ i = i-j+2; j = 1; } //i, j指针后退重新开始匹配
}
if(j > T[0]) return i - T[0];
else return 0;
}
上述算法最坏情况复杂度为O(n*m).例如,当模式串味’00000001’,主串为’00000000000000000000000000001’时,由于模式中前面均为0,与主串相同,每次比较都是在最后一个1不同,然后指针回溯,做了很多无用功。
KMP算法:
改进的算法复杂度为O(n+m)。其改进在于:每当一趟匹配过程中出现字符串比较不等时,不需要回溯i指针,而是利用已经得到的“部分匹配”结果将模式向右“滑动”尽可能远的一段距离后,继续进行比较。
例1:主串为ababcabcacbab, 模式串为abcac
与一般算法相比,本算法的最大特点是i不用回溯,只需将j向右滑动即可,例如,在一般算法中,当i=7,j=5字符比较不等时,须回溯至i=4,j=1。但是经过观察可以发现,在(i=4,j=1),(i=5,j=2),(i=6,j=3)此三者无需进行。因为,从上次匹配结果可以知道,主串中第4,5,6个字符必然是’b’,’c’,’a’(即模式串中的2,3,4个字符)。因为模式串中的第一个字符是a,因此无需和此三者相比,只需将模式向右滑动3个字符继续进行j=7,j=2的字符比较。
推演至一般情形。假设主串为’s1s2…sn’,模式串为’p1p2…pm’,从上例分析可知,为了改进算法,需要解决下述问题:当匹配过程中产生“失配”(即si!=pj)时,模式串“向右滑动”可行的距离多远,换句话说,当主串中第i个字符和模式中第j个字符“失配”时,主串中第i个字符(i不回溯)应与模式中的哪个字符再比较?
假设此时应与模式中第k(k<j)个字符继续比较,则模式中前k-1个字符的子串必须满足下列关系式(1),且不可能存在k’>k满足此关系式(1)
’p1p2…pk-1’ = ’si-k+1si-k+2…si-1’ (1)
而已经得到的“部分匹配”的结果是
’pj-k+1pj-k+2…pj-1’ = ’si-k+1si-k+2…si-1’ (2)
由(1)和(2)可得
’p1p2…pk-1’ = ’pj-k+1pj-k+2…pj-1’ (3)
反之,若模式串中存在满足(3)的两个子串,则当匹配过程中,主串中第i个字符与模式中第j个比较不等时,仅需将模式向右滑动至模式中第k个字符和主串中第i个字符对齐,此时,模式中头k-1个字符的子串’p1p2…pk-1’必定与主串中第i个字符之前长度为k-1的字符’si-k+1si-k+2…si-1’相等,由此,匹配仅需从模式中的第k个字符与主串中的第i个字符继续进行。
若令next[j]=k,则next[j]表明当模式中第j个字符与主串中相应字符“失配”时,在模式中需重新和住传中该字符进行比较的字符的位置。由此,可以引出模式串中的next函数的定义:
由此可以推出下列模式串的next函数值:
在求得模式的next函数之后,匹配可如下进行:假设以指针i和j分别之时主串和模式中正待比较的字符,令i的初值为pos,j 的初值为1。若在匹配过程中si=pj,则i和j分别增1,否则,i不变,j退到next[j]的位置在比较,若相等,则指针各自增1,否则j再退到下一个next位置,以此类推,直至下列两种可能:一种是j退到某个next值时字符比较相等,则指针各自增1,继续进行匹配;另一种是j退到职位0(即模式的第一个字符失配),则此时须将模式向右滑动一个位置,即从主串的下一个字符si+1起和模式重新开始匹配。如下图所示:
KMP算法如下:
int Index(SString S, SString T, int pos)
{ //返回子串T在主串S中的第pos个字符之后的位置。若不存在,则返回。
//其中T非空,<=pos<=StrLength(S).
i = pos; j = 1;
while(i <= S[0] && j <= T[0])
{
if(j == 0 || S[i] == T[j])
{ ++i; ++j; } //继续比较后续字符
else
{ j = next[j]; } //模式串向右移动
}
if(j > T[0]) return i - T[0];
else return 0;
}
KMP算法是在next函数基础上执行的,那么next函数如何求的呢?从上述讨论可见,此函数值仅取决于模式串本身而合相匹配的主串无关。我们可以分析其定义出发用递归的方法求的next函数值。
由定义得, next[1]=0 (5)
设next[j]=k,这表明在模式串中存在下列关系:
’p1p2…pk-1’ = ’pj-k+1pj-k+2…pj-1’ (6)
其中k为满足1<k<j的某个值,并且不可能存在k’>k满足(6),此时next[j+1]=?可能有两种情况:
(1) 若pk = pj,则表明在模式串中
’p1p2…pk-1pk’ = ’pj-k+1pj-k+2…pj-1 pj’ (7)
并且不可能存在k’>k满足此式,即next[j+1]=k+1,即
next[j+1] = next[j]+1 (8)
(2) 若pk != pj,则表明在模式串中
’p1p2…pk-1pk’ != ’pj-k+1pj-k+2…pj-1 pj’ (9)
此时可把next函数求值看成是一个模式匹配问题,整个模式串既是主串又是模式串,而当前在匹配过程中,已有’pj-k+1pj-k+2…pj-1’=’p1p2…pk-1’,则当pj != pk,应将模式向右滑动至以模式中的第next[k]个字符和主串中的第j个字符相比较。若next[k]=k’,且pj = pk’,则说明在主串中第j+1个字符之前存在一个长度为k’(即next[k])的最长子串,和模式中从首字符起长度为k’的子串相等,即
’p1p2…pk-1pk’’ != ’pj-k’+1pj-k+2…pj-1 pj’ (1<k’<k<j) (10)
这就是说next[j+1]=k’+1即
next[j+1] = next[k]+1 (11)
同理,若pj != pk’,则将模式继续向右移动直至将模式中第next[k’]个字符和pj对齐,……,依此类推,直至pj和模式中某个字符匹配成功或者不存在任何k’(1<k’<j)满足(10),则
next[j+1] = 1 (12)
例如,如下图所示,已求得前6个字符的next函数值,前求next[7],因为next[6]=3,有p6!=p3,则需比较p6和p1(因为next[3]=1),这相当于将子串模式向右滑动。由于p6!=p1,而且next[1]=0,所以next[7]=1,而因为p7=p1,则next[8]=2。
根据上述分析所得记过,可得next函数的算法。如下:
void get_next(SString T, int next[])
{
i =1; next[1] = 0; j = 0;
while(i < T[0])
{
if(j == 0 || T[i] == T[j])
{ ++i; ++j; next[i] = j; }
else j = next[j];
}
}
上述算法复杂度为O(m)。但尚有缺陷。例如模式’a a a a b’在和主串’a a a b a a a a b’匹配时当i=4,j=4是s.ch[4] != t.ch[4],由next[j]的指示还需要进行i=4,j=3,i=4,j=2,i=4,j=1这3次比较。实际上,因为模式中第1,2,3个字符和第4个字符都相等,因此不需要再和主串中第4个字符比较,而可以将模式一气向右移动4个字符的位置直接进行i=5,j=1是的字符比较。这就是说,若按上述定义得到next[j]=k,而模式中pj=pk,则当主串中字符si和pj比较不等时,不需要再和pk进行比较,而直接和Pnext[k]进行比较,换句话或,此时的next[j]和next[k]相同。由此可得修正的next函数算法。
void get_nextval(SString T, int nextval[])
{
i =1; next[1] = 0; j = 0;
while(i < T[0])
{
if(j == 0 || T[i] == T[j])
{
if(T[i] != T[j]) nextval[i] = j;
else nextval[k] = next[j];
}
else j = nextval[j];
}
- 字符串篇
- C++字符串转换篇
- C++字符串转换篇
- JS字符串截取篇
- 算法题---字符串篇
- python字符串篇
- 黑马程序员--字符串篇
- 字符串输入篇
- 算法篇-字符串-WERTYU
- Php基础字符串篇
- 黑马程序员-字符串篇
- JS垫脚石-字符串篇
- String 字符串 篇
- 6-字符串篇
- 字符串
- 字符串
- 字符串
- 字符串
- vim终极配置!!!(vimrc)
- j2me入门一些问题
- 屏幕分辨率
- 移动电话充电器
- mtk重要
- 字符串篇
- j2me发短信
- 学好VC++的十大良好习惯
- j2me画图
- E-Learning 和 E-Teaching 的几点思考
- 10--2 内核定时器
- 电子设计(EDA)软件简介
- 计算机的数: 原码, 反码和补码
- 编译环境简介