串的顺序存储
串的定义
//静态数组实现
typedef struct
{
char ch[MAXLEN];
int length;
}SString;
//动态数组实现
typedef struct
{
char *ch;
int length;
}HString;
赋值操作
bool StrAssign(SString &T,char ch[])
{
int chLen = strlen(ch);
if(chLen >MAXLEN)
return false;
T.length = chLen;
for(int i = 1; i <= T.length; i++)
{
T.ch[i] = ch[i-1];
}
return true;
}
复制操作
bool StrCopy(SString &T,SString S)
{
for(int i = 1; i <= S.length; i++)
{
T.ch[i] = S.ch[i];
}
T.length = S.length;
return true;
}
判空操作
bool StrEmpty(SString T)
{
return T.length == 0;
}
求串长
int StrLength(SString T)
{
return T.length;
}
清空操作
bool ClearString(SString &T)
{
T.length=0;
return true;
}
串联接
bool Concat(SString &T,SString S1,SString S2)
{
T.length = S1.length+S2.length;
int i = 1,j=1;
while(i<=S1.length)
{
T.ch[i] = S1.ch[i];
i++;
}
while(i-S1.length<=S2.length)
{
T.ch[i] = S2.ch[i-S1.length];
i++;
}
return true;
}
求子串
bool SubString(SString &Sub,SString S,int pos,int len)
{
if(pos+len-1>S.length)
return false;
Sub.length = len;
for(int i = 1;i<=len;i++)
{
Sub.ch[i] = S.ch[i+pos-1];
}
return true;
}
定位操作
int Index(SString S,SString T)
{
int i = 1,j = 1;
while(i <= S.length && j <= T.length)
{
if(S.ch[i] == T.ch[j])
{
i++;
j++;
}
else
{
i = i - j + 2;
j = 1;
}
}
if(j > T.length)
return i - T.lengthl
else
return 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;
}
串的链式存储
typedef struct
{
char ch;
struct StringNode *next;
}StringNode,*String;
这种方式是不提倡的,因为在32位的计算机中一个指针占4个字节,而每个节点仅保存一个字符(1个字节),所以这样来存储串,会造成巨大的资源浪费。
可以在每一个节点多存储一些字符,这样来减少资源的浪费。
#define Size 10
typedef struct
{
char ch[Size];
struct StringNode *next;
}StringNode,*String;
※朴素匹配算法
暴力枚举所有长度与模式串相同的子串,然后进行比较,直到找到一个完全与模式串匹配的子串,或者所有子串都与模式串不匹配为止。
最坏时间复杂度O(nm) n,m分别为主串和模式串的长度
例如:主串 aaaaaab 模式串aab 每次比较m个字符,需要比较n-m+1次
最好时间复杂度O(m)
字符串首个字符从1开始存储。
int Match(SString S,SString T)
{
int i = 1,j = 1;
while(i <= S.length && j <= T.length)
{
if(S.ch[i] == T.ch[j])
{
i++;
j++;
}
else
{
i = i - j + 2; //如果字符串从0开始,就是 i-j+1
j = 1;
}
}
if(j > T.length)
return i - T.length;
else
return 0;
}
※KMP模式匹配算法
最坏时间复杂度O(n+m) 其中求next数组的时间复杂度为O(m),匹配的过程最坏时间复杂度为O(n)
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;
}
return 0
}
求解next数组
next[x]的含义是当模式串在x处匹配失败后,指针应该移向什么位置。
手算next数组
这里的next数组下标是从1开始的(也有从0开始的)
求解next[1]:
当模式串第一个位置失配时,模式串往前移一位,因此j应该指向0.(所有模式串的next[1]都是0)
求解next[2]:
当模式串第二个位置失配时,模式串也必定往前移一位。所以next[2]=1
求解next[3]:
假设j指向位置失配,模式串右移一位
此时分界线左边的g和o失配,说明失配后模式串不能处于当前这个位置。继续右移
最后,next[3]只能等于1.
求解next[4]:
假设j指向位置失配,模式串右移。
移动之后发现,分界线左边的元素不匹配了,所以模式串仍然要右移。
最终next[4]=1.
求解next[5]:
假设j指向位置失配,模式串不断右移,分界线左边元素不断失配,直到
分界线左边g元素匹配,因此,只需要检测现在j指向的位置是否匹配即可。所以next[5]=2。
求解next[6]:
next[6]与前面求解相同,最后next[6]=1。
练习:
kmp的简单应用
Description
给定两个字符串string1和string2,判断string2是否为string1的子串。
Input
输入包含多组数据,每组测试数据包含两行,第一行代表string1(长度小于1000000),第二行代表string2(长度小于1000000),string1和string2中保证不出现空格。
Output
对于每组输入数据,若string2是string1的子串,则输出string2在string1中的位置,若不是,输出-1。
Sample
Input
abc
a
123456
45
abc
ddd
Output
1
4
-1
思路分析:
模板题。
#include<bits/stdc++.h>
using namespace std;
const int MaxM = 1e6+5;
typedef struct
{
char ch[MaxM];
int length;
}SString;
SString str;//主串
SString pattern;//模式串
int Next[MaxM];//next数组
char tmp[MaxM];
void getNext(int Next[],SString pat)
{
int i = 1,j = 0;
while(i<=pat.length)
{
if(j == 0 || pat.ch[j] == pat.ch[i])
{
j++;
i++;
Next[i] = j;
}
else
{
j = Next[j];
}
}
}
int Index_KMP(SString s,SString pat,int Next[])
{
int i = 1,j = 1;
while(i<=s.length&&j<=pat.length)
{
if(j == 0 || s.ch[i] == pat.ch[j])
{
i++;
j++;
}
else
{
j = Next[j];
}
}
if(j>pat.length)
{
return i-pat.length;
}
return -1;
}
bool StrAssign(SString &S,char s[])
{
S.length = strlen(s);
for(int i = 1; i <= S.length; i++)
{
S.ch[i] = s[i-1];
}
return true;
}
int main()
{
while(scanf("%s",tmp)!=EOF)
{
StrAssign(str,tmp);
scanf("%s",tmp);
StrAssign(pattern,tmp);
getNext(Next,pattern);
cout<<Index_KMP(str,pattern,Next)<<endl;
}
return 0;
}