mirror of
https://github.com/Didnelpsun/CS408.git
synced 2026-10-31 12:24:51 +08:00
串更新
This commit is contained in:
@@ -148,6 +148,7 @@
|
||||
<ClInclude Include="link_list.h" />
|
||||
<ClInclude Include="link_queue.h" />
|
||||
<ClInclude Include="link_stack.h" />
|
||||
<ClInclude Include="link_string.h" />
|
||||
<ClInclude Include="sequence_list.h" />
|
||||
<ClInclude Include="sequence_string.h" />
|
||||
<ClInclude Include="static_link_list.h" />
|
||||
|
||||
@@ -59,4 +59,46 @@ int LocateStaticSequenceString(StaticSequenceString mainstring, StaticSequenceSt
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 求模式串的next数组
|
||||
// 数组0号索引不存值
|
||||
int GetNext(StaticSequenceString string, int *next[]) {
|
||||
int i = 1, j = 0;
|
||||
next[i] = 0;
|
||||
while (i < string.length) {
|
||||
if (j == 0 || string.data[i] == string.data[j]) {
|
||||
++i, ++j;
|
||||
*next[i] = j;
|
||||
}
|
||||
else {
|
||||
if (next[j]) {
|
||||
// 模式串回溯
|
||||
j = *next[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// KMP算法
|
||||
int KMP(StaticSequenceString string1, StaticSequenceString string2) {
|
||||
int i = 1, j = 1;
|
||||
int length = string2.length + 1;
|
||||
int* next = (int*)malloc(length*sizeof(int));
|
||||
GetNext(string2, &next);
|
||||
while (i <= string1.length && j <= string2.length) {
|
||||
if (j == 0 || string1.data[i] == string2.data[j]) {
|
||||
i++, j++;
|
||||
}
|
||||
else {
|
||||
// 模式字符串后移
|
||||
j = next[i];
|
||||
}
|
||||
}
|
||||
if (j > string2.length) {
|
||||
return i - string2.length;
|
||||
}
|
||||
return -1;
|
||||
free(next);
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
+ 使用单独的变量length保存串长。
|
||||
+ 使用data[0]记录串长;使得字符位序与数组下标一致;但是由于char类型一个为一字节大小,所以能表示的数字是0到255,太大的串无法表示。
|
||||
+ 没有表示串长的变量,使用\0表示串结尾,对应ASCII码的0号字符。
|
||||
+ data[0]空余,使用单独的变量length保存串长,这个比较常用。
|
||||
|
||||
## 链串
|
||||
|
||||
@@ -32,7 +33,7 @@
|
||||
|
||||
### 朴素模式匹配算法
|
||||
|
||||
从主串、模式串(子串)的第一个位置开始比较(i=0,j=0),若相等,则 i,j 各自+1,然后比较下一个字符。若不等,主串指针回溯到上一轮比较位置的下一个位置,子串回溯到0,再进行下一次比较。令子串长度为m,主串长度为n:
|
||||
从主串T、模式串P(子串)的第一个位置开始比较(i=0,j=0),若相等,则 i,j各自+1,然后比较下一个字符。若不等,主串指针回溯到上一轮比较位置的下一个位置,子串回溯到0,再进行下一次比较。令子串长度为m,主串长度为n:
|
||||
|
||||
+ 匹配成功的最好时间复杂度:$O(m)$:刚好第一个就匹配上了,总对比次数为子串长度。
|
||||
+ 匹配失败的最好时间复杂度:$O(n-m+1)=O(n-m)=O(n)$:匹配成功之前,每一个与第一个字符都匹配失败。
|
||||
@@ -42,5 +43,20 @@
|
||||
|
||||
KMP算法是对朴素模式匹配算法的优化。
|
||||
|
||||
主要思想是失配时,只有模式串指针回溯,主串指针不变。
|
||||
朴素模式匹配算法的缺点就是当某些子串与模式串能部分匹配时,主串的扫描指针i经常回溯,从而导致时间开销。
|
||||
|
||||
主要思想是失配时,只有模式串指针回溯,主串指针不变,找到失配前模式串的最长公共前后缀并跳转到最大公共后缀开始匹配,且最大公共前后缀要小于左端子串长度。
|
||||
|
||||
+ 前缀:对于字符串A,B,A=B+S,且S非空,则B是A的前缀。
|
||||
+ 后缀:对于字符串A,B,A=S+B,且S非空,则B是A的后缀。
|
||||
+ PMT值:前缀集合和后缀集合的交集中,最长元素的长度。
|
||||
+ 部分匹配表:PMT值集合,字符串所有前后缀的PMT值。
|
||||
|
||||
当一个位置失配时,那么子串前面的所有字符串都是配对的,所以对于子串前面的部分都是已知的了,需要从模式串的最开始开始对比,而一般的模式匹配要从主串的下一个重新开始匹配,但是如果我们找到了主串当前失配位置的前缀和后缀最大重合的地方,即公共前后缀,PMT值,就代表从这里开始就可以匹配了,前面的地方没必要匹配,可以直接多跳几步移动到公共后缀去开始重新匹配。
|
||||
|
||||
当第j个字符匹配失败,由P前1到j-1个字符组成的串记为S,则next[j]=S的最长相等前后缀长度+1(即最大PMT值+1)。特别的next[1]=0,且next[2]肯定为1。
|
||||
|
||||
使用KMP算法时需要先计算不同模式串P的next数组,时间复杂度为$O(m)$,然后使用KMP算法计算,时间复杂度为$O(n)$,从而平均时间复杂度为$O(m+n)$。
|
||||
|
||||
KMP算法对于重复部分比较多的模式串匹配效果更好。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user