生存还是毁灭,这是一个值得思考的问题。
——莎士比亚《哈姆雷特》
特点:
串的逻辑结构和线性表类似,串是限定了元素为字符串的线性表,但是,两者在操作上有很大区别:线性表的操作主要是针对的表内的某一个元素,而串操作主要是对串内的一个子串。
定义:
定长顺序存储定义:
1 2 3 4 5
| typedef struct { char str[maxsize+1]; int length; }Str;
|
定长存储结构不需要分配和释放空间,但是如果想要改变空间大小,需要重新定义结构体。
变长分配存储定义:
1 2 3 4 5
| typedef struct { char *ch; int length; }Str;
|
可以通过这样来使用变长结构体
1 2 3
| Str S; S.length=L; S.ch=(char*)malloc((L+1)*sizeof(char));
|
操作:
1.赋值操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| int strassign(Str& str,char* ch) { if(str.ch) free(str.ch); int len=0; char *c=ch; while(*c) { ++len; ++c; } if(len==0) { str.ch=NULL; str.length=0; return 1; } else { str.ch=(char*)malloc(sizeof(char)*(len+1)); if(str.ch==NULL) return 0; else { c=ch; for(int i=0;i<=len;++i,++c) str.ch[i]=*c; str.length=len; return 1; } } }
|
2.取串长度
1 2 3 4
| int strlength(Str str) { return str.length; }
|
3.串比较操作 (ASCII码和长度的“数值”比较)
1 2 3 4 5 6 7
| int strcompare(Str s1,Str s2) { for(int i=0;i<s1.length&&i<s2.length,++i) if(s1.ch[i]!=s2.ch[i]) return s1.ch[i]-s2.ch[i]; return s1.length-s2.length; }
|
4.串连接操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| int concat(Str& str,Str str1,str2) { if(str.ch) { free(str.ch); str.ch=NULL; } str.ch=(char* )malloc(sizeof(char)*(str1.length+str2.length+1)); if (str.ch==NULL) return 0; int i=0; while(i<str1.length) { str.ch[i]=str1.ch[i]; ++i; } int j=0; while(j<=str2.length) { str.ch[i+j]=str2.ch[j]; ++j; } str.length=str1.length+str2.length; return 1; }
|
5.求子串操作(以下是实现了求str
串从pos
位置开始,len
长度的子串,由substr
返回)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| int substring(Str& substr,Str str,int pos,int len) { if(pos<0||pos>=str.length||len<0||len>str.length-pos) return 0; if(substr.ch) { free(substr.ch); substr.ch=NULL; } if(len==0) { substr.ch=NULL; substr.length=0; return 1; } else { substr.ch=(char*)malloc(sizeof(char)*len+1); int i=pos; int j=0; while(i<pos+len) { substr.ch[j]=str.ch[i]; ++i; ++j; } substr.ch[j]='\0'; substr.length=len; return 1; } }
|
6.串清空操作
1 2 3 4 5 6 7 8 9 10
| int cleanstring(Str &str) { if(str.ch) { free(str.ch); str.ch=NULL; } str.length=0; return 1; }
|
7.字符串模式匹配:
简单模式匹配算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| int naive(Str str,Str substr) { int i=1,j=1,k=i; while(i<str.length && j<=substr.length) { if(str.ch[i]==substr.[j]) { ++i; ++j; } else { j=1; i=++k; } } if(j>substr.length) return k; else return 0; }
|
KMP算法
在模式串j
出发生不匹配时,只需要将F
前移,使得F_l
与F_n
重合即可。
假如F
串的左部和右部有不止一对F_l
和F-n
,我们取第一个满足条件的一对(即较长的一对)。
若想直接由s_k
状态跳转到s_k+1
,只需要移动指针j
即可,i
指针可以保持不变。
next
数组是用来存储,当发生模式串与主串不匹配的时候,下一需要跳转到的位置。
模式串中第j
个位置与主串中的第i
个位置发生不匹配的时候,应从模式串中的第next[j]
个位置与主串第i
个位置重新比较。
KMP算法实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| int KMP(Str str, Str substr ,int next[]) { int i=1,j=1; while(i<str.length&&j<=substr.length) { if(j==0||str.ch[i]==substr.ch[j]) { ++i; ++j; } else { j=next[j]; } } if(j>substr.length) return i-substr.length; else return 0; }
|
next
数组的计算: