串:是由零个或多个字符组成的有限序列,又名字符串。
串的比较是通过组成串的字符之间的编码来进行的,而字符的编码指的是字符在对应字符集中的序号
给定两个串s=“a1a2。。。。。an” t=“b1b2。。。。。。bm”当满足以下条件之一时 s 1.n 例如s=“hap” t=“happy” 因为t比s多出2个字符 所以s 2.存在某个k<=min(m,n) 使的ai=bi(i=1,2。。。。。。,k-1),ak 例如当s=“happen” t=“happy” 因为两串的前4个字符相同 第5个字母(k值),字母e的ASCLL码是101,而字母y的ASCLL码是121 显然e 串的抽象数据类型 ADT 串 (string) Data 串中元素仅由一个字符组成,相邻元素具有前驱和后继关系 Operation StrAssign(T,*chars):生成一个其值等于字符串常量chars的串T StrCopy(T,S):串S存在,由串S赋值得串T ClearString(S):串S存在,将串清空。 StringEmpty(S):若串S为空,返回true,否则返回false StrLength(S):返回串S的元素个数,即串的长度 StrCompare(S,T):若S>T,返回值>0,若S=T 返回0,若S Concat(T,S1,S2):用T返回由S1和S2联接而成的新串。 SubString(Sub,S,pos,len):串S存在,1<=pos<=StrLength(S),且 0<=len<=StrLength(S)-pos+1,用Sub返回串S的 第pos个字符起长度为len的子串。 Index(S,T,pos):串S和T存在,T是非空串,1<=pos<=StrLength(S),若主串S中 存在和串T值相同的子串,则返回它在主串S中第pos个字符之后第 一次出现的位置,否则返回0 Replace(S,T,V):串S,T和V存在,T是非空串,用V替换主串S中出现的所有与T相等 的不重叠的子串 StrInsert(S,pos,T):串S和T存在,1<=pos<=StrLength(S)+1.在串S的第pos个 字符之前插入串T StrDelete(S,pos,len):串S存在,1<=pos<=StrLength(S)-len+1。从串S中删 除第pos个字符起长度为len个子串 endADT Index实现算法 T为非空串,若主串S中第pos个字符之后存在与T相等的子串 则返回第一个这样的子串在S中的位置,否则返回0 int Index(String S,String T,int pos) { int n,m,i; String sub; if(pos > 0) { n = StrLength(S); 得到主串S的长度 m = StrLength(T); 得到子串T的长度 i = pos; while(i <= n-m+1) { SubString(sub,S,i,m); 取主串第i个位置 长度与T相等子串给sub if(StrCompare(sub,T) != 0) 如果两串不相等 ++i; else 如果两串相等 return i; 则返回i值 } } return 0; 若无子串与T相等 返回0 } 串的顺序存储结构 串的顺序存储结构是用一组地址连续的存储单元来存储串中的字符序列的。按照预定义的大小,为每个定义的串变量分配一个固定长度的存储区。一般是用定长数组。\0来表示串值的终结。 串的链式存储结构 一个结点可以存放一个字符或者多个字符,最后一个结点若是未被占满时,可以用#或其他非串值补全 模式匹配:子串的定位操作 对主串的每一个字符作为子串开头,与要匹配的字符串进行匹配。 假设主串S和要匹配的子串T的长度存在S[0]与T[0]。 返回子串T在主串S中第pos个字符之后的位置,若不存在,则函数返回值为0. T非空,1<=pos<=StrLength(S)。 int Index(String S,String T,int pos) { int i = pos; i用于主串S中当前位置下标,若pos不为1。则从pos位置开始匹配 int j = 1; j用于子串T中当前位置下标值 while(i<=S[0] && j <=T[0]) 若i小于S长度且j小于T的长度时循环 { if(S[i] == T[j]) 两字母相等则继续 { ++i; ++j; } else 指针后退重新开始匹配 { i = i-j+2; i退回到上次匹配首位的下一位 j = 1; j退回到子串T的首位 } } if (j >T[0]) return i-T[0]; else return 0; } 时间复杂度O(n+m) 最坏的情况时间复杂度O((n-m)m) KMP模式匹配算法 T=abcabx j 123456 模式串T abcdex next[j] 011111 1.当j=1时 next[1]=0; 2.当j=2时 j由1到j-1就只有字符a,属于其他情况next[2]=1; 3.当j=3时 j由1到j-1串是ab,显然a与b不相等,属其他情况,next[3]=1; 4.以后同理,所以最终此T串next[j]为011111 T=abcabx j 123456 模式串T abcabx next[j] 011123 1.当j=1 next[j]=0 2.当j=2 同上 next[2]=1 3.当j=3 同上 next[3]=1 4.当j=4 同上next[4]=1 5.当j=5 此时j由1到j-1的串是abca 前缀字符a与后缀字符a相等,因此可推算出k值为2因此next[5]=2; 6.当j=6 j由1到j-1的串是abcab,由于前缀字符ab与后缀ab相等所以next[6]=3 根据经验如果前一个字符相等,k值是2,2个相等,k值是3,n个,k值n+1 代码如下 通过计算返回子串T的next数组 void get_next(String T,int *next) { int i,j; i=1; j=0; next[1]=0; while(i { if(j==0 || T[i]==T[j]) T[i]表示后缀的单个字符,T[j]表示前缀的单个字符 { ++i; ++j; next[i]=j; } else j=next[j]; 若字符不相同 则j值回溯 } } 计算出当前要匹配的串T的next数组。 返回子串T在主串S中第pos个字符之后的位置,若不存在,则函数返回值为0 T非空1<=pos<=StrLength(S) int Index_KMP(String S,String T,int pos) { int i=pos; i用于主串S当前位置下标值,若pos不为1,则从pos位置开始匹配 int j=1; j用于子串T中当前位置下标值 int next[255]; get_next(T,next); 定义一next数组 while(i <= S[0] && j<=T[0])若i小于S的长度且j小于T的长度时,循环继续 { if(j==0 || S[i]==T[j]) 两字母相等则继续,与朴素算法增加。j=0判断 { ++i; ++j; } else 指针后退重新开始匹配 { j=next[j]; j退回合适的位置 i值不变 } } if(j>T[0]) return i-T[0]; else return 0; } KMP算法仅当模式与主串之间存在许多 部分匹配 的情况下才体现出它的优势 KMP模式匹配算法改进 求模式串T的next函数修正值并存入数组nextval void get_nextval(String T,int *nextval) { int i,j; i=1; j=0; nextval[1]=0; while(i { if(j==0 || T[i]==T[j]) { ++i; ++j; if(T[i] != T[j]) nextval[i]=j; else nextval[i]=nextval[j]; } else j=nextval[j]; } } 改良后对比 T=ababaaaba j 123456789 模式串T ababaaaba next[j] 011234223 nextval[j] 010104210 1.当j=1 nextval[1]=0 2.当j=2 因第2位字符b的next值是1,而第一位就是a,它们不相等,所以nextval[2]=next[2]=1 维持原值 3.当j=3 因为第三位字符a的next值为1,所以与第一位的a比较得知它们相等,所以nextval[3]=nextval[1]=0; 4.当j=4 第四位的字符b next值为2,所以与第二位的b相比较得到结果是相等,因此nextval[4]=nextval[2]=1 5.j=5 next值为3 第五个字符a 与第三个字符a 相等,因此nextval[5]=nextval[3]=0; 6.当j=6 next值为4 第六个字符a 与第四个字符b不相等,因此nextval[6]=4; 7.当j=7 next值为2, 第七个字符a 与第二个字符b不相等 因此nextval[7]=2 8.当j=8 next值为2,第八个字符b与第二个字符b相等 nextval[8]=nextval[2]=1 9.当j=9时 next值为3 第九个字符a 与第三个字符a 相等 nextval[9]=nextval[3]=1 是在计算出next值的同时,如果a位字符与它next值指向的b位字符相等,则该a位的nextval就指向b位的nextval值,如果不等,则该a位的nextval值就是它自己a位的next的值