串,即字符串(string)是由零个或多个字符组成的有限序列。一般记为S=‘a1a2…an’(n>0)
其中,S是串名,单引号括起来的字符序列是串的值;ai可以是字母、数字或其他字符;串中字符的个数n称为串的长度。n=0时的串称为空串(用空集符号来表示)。
有的地方用双引号(如java、c)有的地方用单引号(如python)
子串:串中任意个连续的字符组成的子序列。
主串:包含子串的串。
字符在主串中的位置:字符在串中的序号。
空串V.S.空格串:
M=’’ M是空串
N=’ ’ N是由三个空格字符组成的空格串,每个空格字符占1B
注意:位序从1开始而不是从0开始。
串是一种特殊的线性表 数据元素之间呈线性关系。
串的数据对象限定为字符集(如中文字符、英文字符、数字字符、标点字符等)
串的基本操作,如增删改查等通常以子串为操作对象。
假设有串T="",S=“iPhone 11 Pro Max?”,W=“Pro”
StrAssign(&T,chars):赋值操作。把串T赋值为chars。
StrCopy(&T,chars):复制操作。由串S复制得到串T。
StrEmpty(S):判空操作。若S为空串,则返回TRUE,否则返回
FALSE。
StrLength(S):求串长。返回串S的元素个数。
ClearString(&S):清空操作。将串S清为空串。
DestroyString(&S):销毁串。将串S销毁(回收存储空间)
Concat(&T,S1,S2):串联接。用T返回由S1和S2联接而成的新串
SubString(&Sub,S,pos,len):求子串。用Sub返回串S的第pos个字符起长度为len的子串。
Index(S,T):定位操作。若主串S中存在与串T值相同的子串,则返回它在主串S中第一次出现的位置;否则函数值为0.
StrCompare(S,T):比较操作。若S>T,则返回值>0;若S=T,则返回值=0;若S<T,则返回值<0。
字符集编码:每一个字符在计算机中对应一个二进制数,比较字符的大小其实就是比较二进制数的大小
串的顺序存储:
代码:
//静态数组实现(定长顺序存储)
#define MAXLEN 255//预定义最大串长为255
typedef struct{
char ch[MAXLEN];//每一个分量存储一个字符
int length;//串的实际长度
}SString;
//动态数组实现堆分配存储
typedef struct{
char *ch;//按串长分配存储区,ch指向串的基地址
int length;//串的长度
}HString;
HString S;
S.ch=(char *)malloc(MAXLEN*sizeof(char));
S.length=0;
分为三个方案对串以及串长进行存储。
方案一:分配一个比较大的内存空间,将变量Length分配在数组末尾。
方案二:分配ch[0]充当Length,其余元素为其后存放。
优点:字符的位序和数组下标相同。
方案三:没有Length变量,以字符’\0‘表示结尾(对应的ASCII码的0)
方案四(教材):ch[0]废弃不用,并分配较大的存储空间,末尾元素存放变量Length
串的链式存储:
代码:
typedef struct StringNode{
char ch;//每一个结点存一个字符
struct StringNode* next;
}StringNode,*String; //存储密度低:每一个字符1B,每一个指针4B
typedef struct StringNode{
char ch[4];
struct StringNode *next;
}StringNode,*String; //存储密度提高
基本操作的实现:
王道主要对于SubString(&Sub,S,pos,len):求子串。用Sub返回串S的第pos个字符起长度为len的子串。以及StrCompare(S,T):比较操作。若S>T,则返回值>0;若S=T,则返回值=0;若S<T,则返回值<0。还有Index(S,T):定位操作。若主串S存在与串T值相同的子串,则返回它在主串S中第一次出现的位置:否则函数值为0。进行详细的概述。
代码:
bool SubString(SString &Sub,SString S,int pos,int len){
//子串范围越界
if(pos+len-1>S.length){
return false;
}
for(int i=pos;i<pos+len;i++)
Sub.ch[i-pos+1]=S.ch[i];
Sub.length=len;
return true;
}
//比较操作。若S>T,则返回值>0;若S=T,则返回值=0,若S<T,则返回值<0
int StrCompare(SString S,SString T){
for(int i=1;i<S.length&&i<T.length;i++){
if(S.ch[i]!=T.ch[i])
return S.ch[i]!=T.ch[i];
}
//扫描过的所有字符都相同,则长度长的串更大
return S.length-T.length;
}
int Index(SString S,SString T){
int i=1,n=StrLength(S),m=StrLength(T);
SString sub;//用于暂存子串
while(i<=n-m+1){
SubString(sub,S,i,m);
if(StrCompare(Sub,T)!=0) ++i;
else return i;//返回子串在主串中的位置
}
return 0;//S中不存在与T相等的子串
}
将上述代码以静态数组为例对串的函数进行整合并实现并做了一个系统判断的函数:
代码:
#include<iostream>
using namespace std;
#define MAXLEN 255
typedef struct {
char ch[MAXLEN];
int length;
}SString;
bool StrAssign(SString T,SString &chars){
if(T.length==0)
return false;
else{
chars=T;
return true;
}
}
bool StrCopy(SString T,SString &chars){
if(T.length==0)
return false;
else{
for(int i=0,j=0;i<T.length;i++){
chars.ch[j++]=T.ch[i];
}
chars.length=T.length;
return true;
}
}
bool StrEmpty(SString S){
if(S.length==0){
return true;
}else{
return false;
}
}
void StrLength(SString &S){
for(int i=0;i<MAXLEN;i++){
if(S.ch[i]!='\0'){
S.length++;
}else{
return;
}
}
}
bool ClearString(SString &S){
if(S.length==0){
return false;
}else{
for(int i=0;i<S.length;i++){
S.ch[i]='\0';
}
S.length=0;
return true;
}
}
bool DestroyString(SString &S){
if(S.length==0){
return false;
}else{
for(int i=0;i<S.length;i++){
S.ch[i]='\0';
}
S.length=0;
return true;
}
}
bool Concat(SString &T,SString S1,SString S2){
if(((S1.length+S2.length)>MAXLEN)||(S1.length==0&&S2.length==0)){
return false;
}else{
for(int i=0;i<S1.length;i++){
T.ch[i]=S1.ch[i];
}
for(int i=0;i<S2.length;i++){
T.ch[i+S1.length]=S2.ch[i];
}
T.length=S1.length+S2.length;
return true;
}
}
bool SubString(SString &Sub,SString S,int pos,int len){
if(pos+len-1>S.length){
return false;
}
for(int i=pos;i<pos+len;i++){
Sub.ch[i-pos+1]=S.ch[i];
Sub.length=len;
return true;
}
}
int StrCompare(SString S,SString T){
for(int i=1;i<S.length&&i<T.length;i++){
if(S.ch[i]!=T.ch[i])
return S.ch[i]!=T.ch[i];
}
return S.length-T.length;
}
int Index(SString S,SString T){
int i=1;StrLength(S);StrLength(T);
int n=S.length;
int m=T.length;
SString sub;
while(i<=n-m+1){
SubString(sub,S,i,m);
if(StrCompare(sub,T)!=0) ++i;
else return i;
}
return 0;
}
bool PrintStr(SString S){
if(S.length==0){
return false;
}else{
for(int i=0;i<S.length;i++){
printf("%c",S.ch[i]);
}
printf("\n");
return true;
}
}
int main(){
int number;
SString str;
printf("请输入您想要创建的字符串:\n");
scanf("%s",&str.ch);
printf("您想要怎么操作字符串,随您的便,请开始你的操作\n");\
bool flag=true;
while(flag){
printf("以下有如下功能进行选择:\n");
printf("1.赋值 2.复制 3.判空 4.求串长 5.清空 6.销毁 7.联接 8.求子串 9.定位 10.比较 11.退出\n");
printf("输入您的所要操作的参数:\n");
scanf("%d",&number);
switch(number){
case 1:{
printf("请输入您想要输入的赋值母串:\n");
SString sstr;
scanf("%s",&sstr.ch);
StrLength(sstr);
StrAssign(sstr,str);
printf("结果如下:\n");
PrintStr(str);
printf("\n\n\n");
break;
}
case 2:{
printf("请输入您想要输入的复制母串:\n");
SString sstr;
scanf("%s",&sstr.ch);
StrLength(sstr);
StrCopy(sstr,str);
printf("结果如下:\n");
PrintStr(str);
printf("\n\n\n");
break;
}
case 3:{
printf("马上开始字符串的判空操作\n");
printf("结果如下\n");
if(StrEmpty(str)){
printf("该串已空!\n");
}else{
printf("该串未空!\n");
}
printf("\n\n\n");
break;
}
case 4:{
printf("马上开始字符串的求串长的操作\n");
printf("结果如下\n");
StrLength(str);
printf("该串的元素个数为:%d\n",str.length);
printf("\n\n\n");
break;
}
case 5:{
printf("马上开始字符串的清空操作\n");
printf("先前的字符串如下:\n");
PrintStr(str);
printf("清空之后的字符串如下\n");
ClearString(str);
PrintStr(str);
printf("\n\n\n");
break;
}
case 6:{
printf("马上开始字符串的销毁操作\n");
printf("先前的字符串如下:\n");
PrintStr(str);
printf("销毁之后的字符串如下\n");
DestroyString(str);
PrintStr(str);
printf("\n\n\n");
break;
}
case 7:{
printf("请输入您想要输入的联接母串:\n");
SString sstr,T;
scanf("%s",&sstr.ch);
StrLength(sstr);
if(Concat(T,str,sstr)){
printf("字符串联接成功!\n");
printf("结果如下:\n");
PrintStr(T);
}else{
printf("字符串联接失败!\n");
}
printf("\n\n\n");
break;
}
case 8:{
printf("请输入您想要的子串的起始地址和长度\n");
int pos,len;
SString substr;
scanf("%d%d",&pos,&len);
SubString(substr,str,pos,len);
printf("结果如下\n");
PrintStr(substr);
printf("\n\n\n");
break;
}
case 9:{
printf("马上开始定位操作\n");
printf("请您输入您想要定位的母串\n");
SString sstr;
scanf("%s",&sstr.ch);
printf("定位结果如下:\n");
printf("该串首先出现在%d处\n",Index(str,sstr));
printf("\n\n\n");
break;
}
case 10:{
printf("马上开始字符串的比较操作\n");
printf("请您先输入您想比较的字符串:\n");
SString sstr;
scanf("%s",&sstr.ch);
if(StrCompare(sstr,str)>0){
printf("您的字符比较串较大\n");
}else if(StrCompare(sstr,str)<0){
printf("您的字符比较串较小\n");
}else{
printf("您的字符比较串跟您的初始串一样大\n");
}
printf("\n\n\n");
break;
}
case 11:{
flag=false;
break;
}
}
}
return 0;
}
代码截图:
该代码略显粗糙,但是基本实现了相应的功能,也实现了所谓的串的功能,但是该代码的耦合度太高,有些的所谓print功能其实本不需要。
用链式存储结构进行对串的存储从而对串的功能实现的代码:
#include<iostream>
#include<cstdlib>
using namespace std;
typedef struct StringNode{
char ch;
struct StringNode* next;
}StringNode,*String;
bool StrEmpty(String S){
if(S==NULL){
return true;
}else{
return false;
}
}
bool StrAssign(String &T,String chars){
while(chars->next!=NULL){
StringNode* p=(StringNode*)malloc(sizeof(StringNode));
p->ch=chars->ch;
p->next=T->next;
T->next=p;
chars=chars->next;
}
}
bool StrCopy(String &T,String chars){
if(chars!=NULL){
T=chars;
return true;
}else{
return false;
}
}
int StrLength(String S){
int result=0;
while(S->next!=NULL){
S=S->next;
result++;
}
return result;
}
bool ClearString(String &S){
if(S->next==NULL)
return false;
else{
while(S->next!=NULL){
StringNode* p=(StringNode*)malloc(sizeof(StringNode));
p=S->next;
S=S->next;
free(p);
}
return true;
}
}
bool DestroyString(String &S){
if(S->next==NULL)
return false;
else{
while(S->next!=NULL){
StringNode* p=(StringNode*)malloc(sizeof(StringNode));
p=S->next;
S=S->next;
free(p);
}
free(S);
return true;
}
}
bool Concat(String &T,String S1,String S2){
StringNode* r=(StringNode*)malloc(sizeof(StringNode));
T=(StringNode*)malloc(sizeof(StringNode));
r=T;
S1=S1->next;
S2=S2->next;
if(S1==NULL||S2==NULL)
return false;
else{
while(S1!=NULL){
StringNode* p=(StringNode*)malloc(sizeof(StringNode));
p->ch=S1->ch;
r->next=p;
r=p;
S1=S1->next;
}
while(S2!=NULL){
StringNode* p=(StringNode*)malloc(sizeof(StringNode));
p->ch=S2->ch;
r->next=p;
r=p;
S2=S2->next;
}
r->next=NULL;
return true;
}
}
bool SubString(String &Sub,String S,int pos,int len){
StringNode* r=(StringNode*)malloc(sizeof(StringNode));
Sub=(StringNode*)malloc(sizeof(StringNode));
r=Sub;
S=S->next;
int length=StrLength(S);
if((pos+len-1)>length){
return false;
}else{
int i=0;
while(i<pos){
S=S->next;
i++;
}
while(i<len+pos){
StringNode* p=(StringNode*)malloc(sizeof(StringNode));
p->ch=S->ch;
r->next=p;
r=p;
S=S->next;
i++;
}
r->next=NULL;
return true;
}
}
int StrCompare(String S,String T){
S=S->next;
T=T->next;
int len1=StrLength(S);
int len2=StrLength(T);
int i=0;
while(S!=NULL&&T!=NULL){
if((S->ch)!=(T->ch))
return ((S->ch)!=(T->ch));
S=S->next;
T=T->next;
}
return len1-len2;
}
int Index(String S,String T){
int i=1,n=StrLength(S),m=StrLength(T);
String sub;
while(i<n-m+1){
SubString(sub,S,i,m);
if(StrCompare(sub,T)!=0)
++i;
else return i;
}
return 0;
}
void StrCreate(String &S){ //创建字符串
printf("请您创建一个字符串!!!!\n");
printf("以输入#结束!!!!\n");
S=(StringNode*)malloc(sizeof(StringNode));
StringNode* r=(StringNode*)malloc(sizeof(StringNode));
r=S;
char chr;
scanf("%c",&chr);
while(chr!='#'){
StringNode* p=(StringNode*)malloc(sizeof(StringNode));
p->ch=chr;
r->next=p;
r=p;
scanf("%c",&chr);
}
r->next=NULL;
}
void PrintStr(String S){
S=S->next;
while(S!=NULL){
printf("%c",S->ch);
S=S->next;
}
printf("\n");
}
int main(){
String str;
StrCreate(str);
PrintStr(str);
bool flag=true;
int number;
while(flag){
printf("以下有如下功能进行选择:\n");
printf("0.打印 1.赋值 2.复制 3.判空 4.求串长 5.清空 6.销毁 7.联接 8.求子串 9.定位 10.比较 11.退出\n");
printf("输入您的所要操作的参数:\n");
scanf("%d",&number);
switch(number){
case 0:{
PrintStr(str);
printf("\n\n\n");
break;
}
case 1:{
printf("请输入您想要输入的赋值母串:\n");
String sstr;
StrCreate(sstr);
StrAssign(sstr,str);
printf("结果如下:\n");
PrintStr(str);
printf("\n\n\n");
break;
}
case 2:{
printf("请输入您想要输入的复制母串:\n");
String sstr;
StrCreate(sstr);
StrCopy(str,sstr);
printf("结果如下:\n");
PrintStr(str);
printf("\n\n\n");
break;
}
case 3:{
printf("马上开始字符串的判空操作\n");
printf("结果如下\n");
if(StrEmpty(str)){
printf("该串已空!\n");
}else{
printf("该串未空!\n");
}
printf("\n\n\n");
break;
}
case 4:{
printf("马上开始字符串的求串长的操作\n");
printf("结果如下\n");
int len=StrLength(str);
printf("该串的元素个数为:%d\n",len);
printf("\n\n\n");
break;
}
case 5:{
printf("马上开始字符串的清空操作\n");
printf("先前的字符串如下:\n");
PrintStr(str);
printf("清空之后的字符串如下\n");
ClearString(str);
PrintStr(str);
printf("\n\n\n");
break;
}
case 6:{
printf("马上开始字符串的销毁操作\n");
printf("先前的字符串如下:\n");
PrintStr(str);
printf("销毁之后的字符串如下\n");
DestroyString(str);
PrintStr(str);
printf("\n\n\n");
break;
}
case 7:{
printf("请输入您想要输入的联接母串:\n");
String sstr,T;
getchar();
StrCreate(sstr);
PrintStr(sstr);
if(Concat(T,str,sstr)){
printf("字符串联接成功!\n");
printf("结果如下:\n");
PrintStr(T);
}else{
printf("字符串联接失败!\n");
}
printf("\n\n\n");
break;
}
case 8:{
printf("请输入您想要的子串的起始地址和长度\n");
int pos,len;
String substr;
scanf("%d%d",&pos,&len);
SubString(substr,str,pos,len);
printf("结果如下\n");
PrintStr(substr);
printf("\n\n\n");
break;
}
case 9:{
printf("马上开始定位操作\n");
printf("请您输入您想要定位的母串\n");
String sstr;
StrCreate(sstr);
printf("定位结果如下:\n");
printf("该串首先出现在%d处\n",Index(str,sstr));
printf("\n\n\n");
break;
}
case 10:{
printf("马上开始字符串的比较操作\n");
printf("请您先输入您想比较的字符串:\n");
String sstr;
StrCreate(sstr);
if(StrCompare(sstr,str)>0){
printf("您的字符比较串较大\n");
}else if(StrCompare(sstr,str)<0){
printf("您的字符比较串较小\n");
}else{
printf("您的字符比较串跟您的初始串一样大\n");
}
printf("\n\n\n");
break;
}
case 11:{
flag=false;
break;
}
default:{
printf("参数无效,请您重新再输!\n");
continue;
}
}
}
return 0;
}
代码运行截图:
串的模式匹配:在主串中找到模式串相同的子串,并返回其所在位置。
朴素模式匹配算法(简单模式匹配算法)思想:
将主串中与模式串长度相同的子串搞出来,挨个与模式串对比当子串与模式串某个对应字符不匹配时,就立即放弃当前子串,转而检索下一个子串。
若模式串长度m,主串长度为n,则直到匹配成功/匹配失败最多需要
(n-m+1)*m 次比较
最坏时间复杂度:O(nm)
最坏情况:每个子串的前m-1个字符都和模式串匹配,只有第m个字符不匹配
比较好的情况,每个子串的第一个字符就与模式串不匹配
实现代码:
int Index(SString S,SString T){//咸鱼学长的代码
int k=1;
int i=k,j=1;
while(i<=S.length&&j<=T.length){
if(S.ch[i]==T.ch[j]){
++i;
++j;
}else{
k++;
i=k;
j=1;
}
}
if(j>T.length){
return k;
}else{
return 0;
}
}
int Index(SString S,SString T){//课本的代码
// int k=1;
int i=1,j=1;
while(i<=S.length&&j<=T.length){
if(S.ch[i]==T.ch[j]){
++i;
++j;
}else{
// i=k;
i=i-j+2;//用i-j+2替换k
j=1;
}
}
if(j>T.length){
return i-T.length;
}else{
return 0;
}
}
KMP算法:
int Index_KMP(SString S,SString T,int next[]){
int i=1,j=1;
while(i<=S.length&&j<=T.length){
if(j==0||S.ch[i]==T.ch[j]){
++i;
++j;
}
else
j=next[i];
}
if(j>T.length)
return i-T.length;
else
return 0;
}
KMP求next数组代码:
void get_next(SString T,int next[]){
int i=1;j=0;
next[1]=0;
while(i<T.length){
if(j==0||T.ch[i]==T.ch[j]){
++i;++j;
next[i]=j;
}
else
j=next[j];
}
}
上述求next数组的代码不管怎么看都不太懂,尽管知道该代码的数组下标是1为开始的。
又做了一个程序来跟进程序的运行过程,但是还是看不懂,只是发现了,求next数组的一些递归进程,最后结合B站学习视频终于弄明白了,不容易!
下述代码:
#include<iostream>
#include<vector>
using namespace std;
#define MAXLEN 255 //定义字符数组的最大长度
typedef struct {
char ch[MAXLEN];
int length=6;//默认该字符串的长度为6,方便操作。
}SString;
void get_next(SString T,int next[]){
int i=1,j=0;
next[1]=0;
int lun=1;
while(i<T.length){
printf("i=%d j=%d\n",i,j);
printf("T.ch[i=%d]=%c T.ch[j=%d]=%c\n",i,T.ch[i],j,T.ch[j]);
if(j==0||T.ch[i]==T.ch[j]){
printf("++i\n");
printf("++j\n");
++i;
++j;
next[i]=j;
printf("next[i=%d]=(j=%d)\n\n",i,j);
}
else{
printf("%d=next[%d]\n\n",j,j);
j=next[j];
}
printf("第%d轮结束!\n\n\n",lun++);
}
}
int main(){
printf("请输入您所给的字符!\n");
SString S;
scanf("%s",&S.ch);
for(int i=0;i<S.length;i++){
printf("%c",S.ch[i]);
}
printf("\n");
int next[S.length];
get_next(S,next);
for(int i=0;i<S.length;i++){
printf("next[%d]=%d\n",i,next[i]);
}
printf("\n");
return 0;
}
代码运行截图:
B站一位up主的视频关于求next数组的代码理解讲得非常好!
之后自己实现KMP算法来复习KMP算法吧。
自己写了一个下标以1开始的KMP算法(记得应该数据结构课本数组下标也是1为开始)。。。。。
KMP算法实现代码:
#include<iostream>
#include<cstdlib>
using namespace std;
#define MAXSIZE 255//定义一个字符串的最大长度为255
//定义一个自己定义的字符串的结构体
typedef struct {
char ch[MAXSIZE];
int length;
}SString;
//由于本代码需要求字符串的长度因而增加了一个求长赋值函数,且求长函数是以1开始的
void StrLength(SString &T){
int result=0;
for(int i=1;i<MAXSIZE;i++){
if(T.ch[i]!='1'){
result++;
}else{
break;
}
}
T.length=result;
}
//本代码主要讨论用KMP算法来求模式串在所给串的匹配的起始位置
int Index_KMP(SString S,SString T,int next[]){
int i=1,j=1;
while(i<=S.length&&j<=T.length){
if(j==0||S.ch[i]==T.ch[j]){
i++;j++;
}else{
j=next[j];
}
}
if(j>T.length){
return i-T.length;
}else{
return 0;
}
}
void get_next(SString S,int next[]){//注意该下标是以1开始的
int i=1,j=0;
next[1]=0;
while(i<=S.length){
if(j=0||(S.ch[i]==S.ch[j])){
next[i++]=j++;//最好的结果是两者相等,之后就是下一个next数组的元素的值加一
}else{
j=next[j];
}
}
}
void PrintStr(SString S){
for(int i=1;i<=S.length;i++){
printf("%c",S.ch[i]);
}
printf("\n");
}
int main(){
SString S;
SString T;
//输入的模式串和母串尽量有公共部分,并且输入的时候第一个字符不识别,并以‘1’结束!
printf("请输入您的母串!且第一个字符不识别,以‘1’结束!\n");
scanf("%s",S.ch);
printf("母串:\n");
StrLength(S);//母串
printf("母串的长度为%d\n",S.length);
PrintStr(S);
printf("请输入您的模式串!且第一个字符不识别,以‘1’结束\n");
scanf("%s",T.ch);
printf("模式串:\n");
StrLength(T);//模式串
printf("模式串的长度为%d\n",T.length);
PrintStr(T);
int len1=T.length;
int next[len1+1];//数组都是从1开始的
get_next(T,next);
for(int i=1;i<=len1;i++){
printf("%d ",next[i]);
}
printf("\n");
int pos=Index_KMP(S,T,next);
printf("该模式串在母串的起始为%d\n",pos);
return 0;
}
代码截图:
但是上述代码在匹配复杂字符串的时候会超时!
下面改进代码,采用nextval数组进行优化。
nextval数组的求法:
先算出next数组
先令nextval[1]=0
for(int i=2;j<=T.length;j++){
if(T.ch[next[i]]==T.ch[j])
nextval[j]=nextval[next[j]];
else
nextval[j]=next[j];
}
KMP优化代码:
#include<iostream>
#include<cstdlib>
using namespace std;
#define MAXSIZE 255//定义一个字符串的最大长度为255
//定义一个自己定义的字符串的结构体
typedef struct {
char ch[MAXSIZE];
int length;
}SString;
//由于本代码需要求字符串的长度因而增加了一个求长赋值函数,且求长函数是以1开始的
void StrLength(SString &T){
int result=0;
for(int i=1;i<MAXSIZE;i++){
if(T.ch[i]!='1'){
result++;
}else{
break;
}
}
T.length=result;
}
//本代码主要讨论用KMP算法来求模式串在所给串的匹配的起始位置
int Index_KMP(SString S,SString T,int next[]){
int i=1,j=1;
while(i<=S.length&&j<=T.length){
if(j==0||S.ch[i]==T.ch[j]){
i++;j++;
}else{
j=next[j];
}
}
if(j>T.length){
return i-T.length;
}else{
return 0;
}
}
int Index_KMP_val(SString S,SString T,int nextval[]){
int i=1,j=1;
while(i<=S.length&&j<=T.length){
if(j==0||S.ch[i]==T.ch[j]){
i++;j++;
}else{
j=nextval[j];
}
}
if(j>T.length){
return i-T.length;
}else{
return 0;
}
}
void get_next(SString S,int next[]){//注意该下标是以1开始的
int i=1,j=0;
next[1]=0;
while(i<=S.length){
if(j=0||(S.ch[i]==S.ch[j])){
next[i++]=j++;//最好的结果是两者相等,之后就是下一个next数组的元素的值加一
}else{
j=next[j];
}
}
}
void get_nextval(int nextval[],int next[],SString T){//注意该下标是以1开始的
nextval[1]=0;
for(int i=2;i<=T.length;i++){
if(T.ch[next[i]]=T.ch[i])
nextval[i]=nextval[next[i]];
else
nextval[i]=next[i];
}
}
void PrintStr(SString S){
for(int i=1;i<=S.length;i++){
printf("%c",S.ch[i]);
}
printf("\n");
}
int main(){
SString S;
SString T;
//输入的模式串和母串尽量有公共部分,并且输入的时候第一个字符不识别,并以‘1’结束!
printf("请输入您的母串!且第一个字符不识别,以‘1’结束!\n");
scanf("%s",S.ch);
printf("母串:\n");
StrLength(S);//母串
printf("母串的长度为%d\n",S.length);
PrintStr(S);
printf("请输入您的模式串!且第一个字符不识别,以‘1’结束\n");
scanf("%s",T.ch);
printf("模式串:\n");
StrLength(T);//模式串
printf("模式串的长度为%d\n",T.length);
PrintStr(T);
int len1=T.length;
int next[len1+1];
int nextval[len1+1];//数组都是从1开始的
get_next(T,next);
get_nextval(nextval,next,T);
int pos=Index_KMP_val(S,T,nextval);
printf("该模式串在母串的起始为%d\n",pos);
return 0;
}
代码截图:
KMP算法考点总结:
KMP算法当子串和模式串不匹配时,主串指针i不回溯,模式串指针j=next[j]
算法平均时间复杂度:O(n+m)
next数组手算的方法:当第j个字符匹配失败,由前1~j-1个字符组成的串记为S,则:next[j]=S的最长相等后缀长度+1
特别地,next[i]=0