字符匹配问题
有个一主串 S = {a,b,c,a,c,a,b,d,c},模式串T={a,b,d};请找到模式串在主串中第一次出现的位置;
提示:不需要考虑字符串大小写问题,字符均为小写字母;
分析:
#include <stdio.h>
#include "string.h"
#include "stdlib.h"
#include "math.h"
#include "time.h"
//#include "ctype.h"
#define OK 1
#define ERROR 0
#define TURE 1
#define FALSE 0
1、BF算法 - 暴风算法
#define MAXSIZE 40 //初始化分配空间
typedef int Status; //Status 是函数的类型,其值是函数结果状态码,如OK 等
typedef int ElemType; // ElemType 类型是根据实际情况而定,这里假设为int
typedef char String[MAXSIZE+1];//0 号单位元存放串的长度,所以申请内存的时候要在初始化空间大小下 +1
1、生成一个其值等于chars 的串T
//生成一个其值等于chars 的串T
Status StrAssign(String T, char * chars){
if (strlen(chars)>MAXSIZE) {
return ERROR;
}else{
T[0] = strlen(chars);
for (int i=1; i<=T[0]; i++) {
T[i] = *(chars+i-1);
}
return OK;
}
}
2、清空串
//清空串
Status ClearString(String S){
S[0] = 0;//串长度置为0即可
return OK;
}
3、输出字符串 T
//输出字符串 T
void StrPrint(String T){
for (int i=1; i<=T[0]; i++) {
printf("%c ",T[i]);
}
printf("\n");
}
3、输出Next数组值
//输出Next数组值
void NextPrint(int next[],int length){
for (int i=1; i<=length; i++) {
printf("%d ",next[i]);
}
printf("\n");
}
4、返回串的元素个数
用串的第一位存放串的长度
//返回串的元素个数
int StrLength(String S){
return S[0];
}
字符串匹配问题
思路:
1、分别l利用指针i 和j 指示主串S 和模式T 中当前等待比较的字符位置,i初值为pos ,j初值为1;
2、如果2个串均比较到了串尾,即i 和 j均小于等于S 和T的长度时,则循环执行以下的操作
(1)、S[i] 和 T[j]比较,若相等,则i 和 j分别指示串中下一个位置,继续比较后续的字符;
(2)、若不相等,指针后退重新开始匹配。从主串的下一个字符串(i= i-j+2)起再重新和模式第一个字符(j = 1)比较;
3、如果 j > T.length,说明模式T 中的每个字符串依次和主串S 栈中的一个连续字符序列相等,则匹配成功,返回和模式T 中第一个字符一样的字符在主串中S 中的序号(i-T.length);否则匹配失败,返回0;
int Index_BF(String S, String T, int pos){
// i用于主串S 中当前位置下标值,若pos 不为1,则从pos 位置开始匹配
int i = pos;
// j 用于子串T 中当前位置下标值
int j = 1;
// 若i 小于S 的长度并且j 小于T 的长度时,循环继续
while (i<= S[0] && j<= T[0]) {
// 比较的二个字母相等,则继续比较
if (S[i] == T[j]) {
i++;
j++;
}else{
// 不相等,则指针后退重新匹配
// i 退回到上次匹配的首位之的下一位
// 加1,是因为子串的首位是从1开始的
// 再加1,是因为从上次匹配的首位的下一位开始
i = i-j+2;
// j退到子串T的首位
j = 1;
}
}
// 如果j>T[0],则找到了匹配模式(因为子串遍历完了都是相等的)
if (j>T[0]) {
// i母串遍历的位置 - 模式字符串长度 = index 位置
return i-T[0];
}else{
// 主串中没有匹配的字符串
return -1;
}
}
int main(int argc, const char * argv[]) {
//************ BF算法 ***************
int i,*p;
String s1,s2;
StrAssign(s1, "abcdex");
printf("s1子串为");
StrPrint(s1);
StrAssign(s2, "ex");
printf("s2子串为");
StrPrint(s2);
i = Index_BF(s1, s2, 1);
printf("i = %d\n",i);
return 0;
}
//*********************************
s1子串为a b c d e x
s2子串为e x
i = 5
2.RK算法
//RK算法
//表示进制
#define d 26
1、二次确认,避免哈希冲突 造成错误
//1、为了杜绝哈希冲突,当发现模式串和子串的HashValue 是一样的时候,还是需要二次确认2 个字符串是否相等。
//S 主串 i 相同的字符串首位下标 P 子串 m 子串长度
int isMatch(char *S, int i, char *P, int m){
int is,ip;
// 主串从开始,子串从0开始。均为未遍历到子串对应长度的的时候,如果出现不相同就不匹配。否者匹配
for (is =i,ip=0; is != m+i && ip != m; is++, ip++) {
if (S[is] != P[ip]) {
return 0;
}
}
return 1;
}
2、算出 d进制下的最高位
//2、算出 d进制下的最高位
//d^(m-1)位的值
int getMaxValue(int m){
int h =1;
for (int i =0; i< m-1; i++) {
h = (h * d);
}
return h;
}
字符串匹配的 RK算法
//若成功匹配返回主串中的偏移,否则返回-1
int RK(char *S, char *P){
//1、n:主串长度 m:子串长度
int n = (int)strlen(S);
int m = (int)strlen(P);
printf("主串长度为:%d \n子串长度为:%d\n",n,m);
// A模式串的哈希值;St.主串分解子串的哈希值;
// 无符号整形
unsigned int A = 0;
unsigned int St = 0;
// 2、求得子串中 0 -m字符串的哈希值[计算子串与主串0-m]的哈希值
// 循环[0,m]获取模式串A 的HashValue 以及主串第一个[0,m] 的hasValue
//例如:
//此时主串:"abcaadddabceeffccdd" 它的[0,2]是ab
//此时模式串:"cc"
//cc = 2 * 26^1 + 2 *26 ^0 = 52+2 = 54;
//ab = 0 * 26^1 + 1 *26^0 = 0+1 = 1;
for (int i=0; i !=m; i++) {
//第一次 A= 0*26+2;
//第二次 A= 2*26+2;
A = (d*A + (P[i] - 'a'));
//第一次 st = 0*26+0
//第二次 st = 0*26+1
St = (d*St + (S[i]-'a'));
}
// 3、获取d^(m-1) 值(因为后面多处用到,避免重复计算)
int hValue = getMaxValue(m);
// 4、遍历[0,n-m],判断模式串HashValue A是否和其他子串的HashValue 一致。
// 不一致则继续求得下一个HashValue
// 如果一直则进入二次确认判断,2个字符串是否真正相等。否者哈希值错误继续判断
// 注意细节
// (1)、在进入循环时,就已经得到了子串的哈希值以及主串的[0,m] 的哈希值,可以直接进入第一轮比较;
// (2)、哈希值相等后,利用在循环之前已经计算好的st[0] 来计算后面的st[1];
// (3)、如果不相等,利用在循环之前已经计算好的st[0] 来计算后面的st[1];
// (4)、在对比过程,并不是一次性把所有的主串都求解好hash值。而是借助s[i] 来求解 s[i+1]。 简单说就是一边比较哈希值,一边计算哈希值;
for (int i=0; i<=n; i++) {
if (A == St) {
if (isMatch(S, i, P, m)) {
// 加1 因为从1开始
return i+1;
}
}
// 取出原来的第一位,加上新添加的最后一位。
// 例:
// 主串:abc 子串:cc
// 原:0*26^1 + 1 = St
// 计算:St =(St - 0*26^1)*26 + 2
St = ((St - hValue*(S[i]-'a'))*d + (S[i+m] - 'a'));
}
return -1;
}
int main(int argc, const char * argv[]) {
//************ RK算法 ***************
char *buf="abcababcabx";
char *ptrn="cab";
printf("主串为%s\n",buf);
printf("子串为%s\n",ptrn);
int index = RK(buf, ptrn);
printf("find index : %d\n",index);
return 0;
}
//***********************************
主串为abcababcabx
子串为cab
主串长度为:11
子串长度为:3
find index : 3