串的定义:
串(string)是由零个或者多个字符组成的有限序列, 又名叫字符串。
串的比较:
字典序
串的ADT:
串的顺序结构:
使用的是数组的方式来存储, 但是一般都会动态的对数组的大小进行扩容。
串的链式存储结构:
一个Node存储多少长度的字符需要根据实际情况作出调整, 每个Node大小会直接影响到字符串处理的效率。
链式存储除了在连接字符串的时候更加方便以外,整体上不如顺序存储灵活, 性能也不如顺序存储结构好。
模式匹配算法:
BD 匹配 : 低效。
KMP:
不移动主串的 i , 仅仅针对失配位置,对模式串进行调整。
!!! 注意: 此图是本书中使用的自定的串结构, 下标从1开始的 , 下方代码是使用C风格字符串, 下标从0开始的, 因此上述next[j] 公式修改如下:
KMP 算法代码实现:
以普通的C风格字符串作为例子。
#include <stdio.h> #include <string.h> #include <stdlib.h> #define NDEBUG /*需要调试信息输出请注释此宏*/ /*获取next数组*/ void get_next(const char *t, int *next) { int i,j; i = 0; j = -1; /*调整j为-1,代表主串从失配位置下一个位置 在 和模式串重头开始匹配*/ next[0] = -1; int t_len = strlen(t); while(i < t_len) { if(-1 == j || t[i] == t[j]) { ++i; ++j; next[i] = j; }else{ j = next[j]; } } } /*匹配, 在main串中匹配pattern*/ int subString(const char *s, const char *t) { int i = 0; /*指向mian串字符位置的指针*/ int j = 0; /*指向pattern串字符位置的指针*/ int s_len = strlen(s); int t_len = strlen(t); int * next = (int *)malloc(sizeof(int) * (t_len)); get_next(t, next); while(i < s_len && j < t_len) { /*等于-1 说明将i+1的位置 和 模式串的0位置的字符串在进行匹配就行了*/ if( -1 == j || s[i] == t[j]) { i++; j++; } else /*失配调整位置*/ { /*下方代码块儿 输出调试信息, 方便理解KMP算法 , 可以删除*/ { #ifndef NDEBUG int len = i - j; int k; printf("-------------------lose--------------------------\n"); printf(" i : %d -- j : %d \n", i, j); printf("%s\n", s); for(k = 0; k<i;++k) printf(" "); printf("^\n"); for(k = 0; k<len;++k) printf(" "); printf("%s\n", t); printf("--------------------------------------------------\n"); #endif } j = next[j] } } free(next); if(j >= t_len) return i - t_len; else return -1; } int main() { const char *t = "abcade"; const char *s = "abxabdabcade"; int result = subString(s, t); printf("%d\n",result); return 0; }
KMP优化:
例子1:
s : da a a a a a b c x a a a a a a a
t : a a a a a a a
next : -1 0 1 2 3 4
例子2:
s:aabaabaabaaxaabaabaabaab
s:aabaabaabaab
如上方的例子:
在 i = 5 且 j = 5 的位置发生了失配, 那么 j 会调整到 j = 4 继续 与 i = 5 匹配 又发生失配, j又
调整到 j = 3 一直调整到 j = 0 的时候-1标志才会让 i 加1, 进而完成一次 I的调整 。可见从
s[5] != t[5] 的那次失配的其, next调整到4, 3 , 2 , 1 完全是多余的, 直接调整到 0 就行啦。
则
那么求next数组的算法进行如下优化:
1 void get_next(const char *t, int *next) 2 { 3 int i,j; 4 i = 0; 5 j = -1; /*调整j为-1,代表主串从失配位置下一个位置 在 和模式串重头开始匹配*/ 6 next[0] = -1; 7 int t_len = strlen(t); 8 9 while(i < t_len) 10 { 11 if(-1 == j || t[i] == t[j]) 12 { 13 ++i; 14 ++j; 15 /*i = 1 时候直接设置为0, 正好j的值为0*/ 16 /*否则判断 next[i] 与 next[i - 1]就行了*/ 17 next[i] = ((i != 1 && str[i] == str[j]) ? next[j] : j); 18 } 19 }else{ 20 j = next[j]; /*失配, 则将 j 回溯,这么写是确保*/ 21 } 22 } 23 }