文章目录
一、参考知识
二、代码部分
1. 头文件:string_pattern_matching.h
//
// Project: String_Pattern_Matching
// File: string_pattern_matching.h
// IDE: CLion
// Created by RichardLau_Cx on 2021/5/20.
//
//#ifndef STRING_PATTERN_MATCHING_STRING_PATTERN_MATCHING_H
//#define STRING_PATTERN_MATCHING_STRING_PATTERN_MATCHING_H
//
//#endif //STRING_PATTERN_MATCHING_STRING_PATTERN_MATCHING_H
/*************************************************************
串操作的实现 头文件
**************************************************************/
#define MAXSTRLEN 255 // 定义串最大长度
typedef char SString[MAXSTRLEN + 1]; // 定义串,并且将SString替换为char数组类型,0号单元存放串的长度, 用'\0'作结束符
// 求子串
void SubStr(SString t, SString s, int i, int len);
// 以字符数组存储字符串,实现朴素的模式匹配算法
int Index(char S[], char T[], int pos);
// n模式串的next函数
void Get_next(char *p, int next[]);
// KMP模式匹配算法,设模式串第一个字符的下标为0
int Index_KMP(char *s, char *p, int pos, int next[]);
2. 主函数文件:main.cpp
//
// Project: String_Pattern_Matching
// File: functional_operation.cpp
// IDE: CLion
// Code style: AllMan Style - BSD
// Created by RichardLau_Cx on 2022/5/20.
//
#include <iostream>
#include <cstring>
#include "string_pattern_matching.h"
int main() {
SString s, t;
int i, len;
int goOn=1; // 是否继续使用
int opt; // 功能选项
int pos = 0; // 索引位置
while (goOn) {
printf("\n欢迎来到串的世界,请选择你想要的操作:\n"
"1. 求子串\n"
"2. 朴素的模式匹配算法\n"
"3. KMP模式匹配算法\n\n");
scanf("%d", &opt);
switch (opt) {
case 1:
printf("\n输入主串:\n");
scanf("%s", s + 1);
s[0] = strlen(s + 1); // 获取输入串的长度(int -> char)
// itoa(strlen(s+1), s, 10); // 获取输入串的长度 -得修进,目前不可行
printf("\n截取起点(索引):\n");
scanf("%d", &i);
printf("\n截取长度:\n");
scanf("%d", &len);
SubStr(t, s, i, len);
// printf("主串长度为: %d\n", s[0]);
// printf("子串长度为: %d", t[0]);
break;
case 2:
printf("\n输入主串:\n");
scanf("%s", s);
printf("\n输入模式串:\n");
scanf("%s", t);
pos = Index(s, t, pos); // 返回模式串在主串中从pos开始的位置(下标)
if (pos == -1)
{
printf("\n匹配失败\n");
}
else
{
printf("\n模式串在主串中开始的位置pos (下标)为:[%d]\n", pos);
}
break;
case 3:
int i;
int next[MAXSTRLEN+1];
printf("\n输入主串:\n");
scanf("%s", s); // 主串
printf("\n输入模式串:\n");
scanf("%s", t);
pos = Index_KMP(s, t, 1, next);
if (pos == -1)
{
printf("\n匹配失败\n");
}
else
{
printf("\n模式串在主串中开始的位置pos (下标)为:[%d]\n", pos);
}
break;
}
printf("\n还要继续使用吗(是为1,否为0)?\n");
scanf("%d", &goOn);
}
return 0;
}
3. 功能操作文件:functional_operation.cpp
//
// Project: String_Pattern_Matching
// File: functional_operation.cpp
// IDE: CLion
// Code style: AllMan Style - BSD
// Created by RichardLau_Cx on 2022/5/20.
//
#include <cstring>
#include <cstdio>
#include "string_pattern_matching.h"
void SubStr(SString t, SString s, int i, int len)
{
/** 从s的第i个字符开始,截取长度为len的子串存入t中
* 其中: 1 <= i <= sLen, 0 <= len <= sLen - i + 1
* 若i和len超出了取值范围,则输出“error”;否则输出字串t
*/
int k = 1;
int j = i;
if (i < 1 || i > s[0] || len < 0 || len > s[0] - i + 1)
{
/**
* 1. [0]号位置存放着串的长度
* 2. 截取开始字符位置超过串长
* 3. 截取长度为0
* 4. 截取长度大于从i位置开始的剩余串长度
*/
printf("error");
return;
}
for (k = 1, j = i; k <= len; j++, k++)
{
t[k] = s[j]; // 从主串中对应位置i开始,取len个元素进字串
}
t[0] = len; // 截取长度,即子串长度
t[k] = '\0'; // 串尾存储一个结束符'\0'
printf("\n所截取的子串为:%s\n", t+1);
// puts(t+1); // 相当于从索引为1的地址开始输出子串
}
int Index(char S[], char T[], int pos)
{ /** 查找并返回模式串T在主串S中,从pos开始的位置(下标),若T不是S的子串,则返回-1
* 模式串T和主串S第一个字符的下标为0
*/
int i = pos;
int j = 0; // i,j分别用于指出主串字符和模式串字符的位置
int sLen, tLen; // 主串和模式串的长度
sLen = strlen(S);
tLen = strlen(T);
// printf("i: %d\n", i);
// printf("sLen: %d\n", sLen);
// printf("tLen: %d\n", tLen);
while (i < sLen && j <tLen)
{ // 都没有匹配至尾部时
if (S[i] == T[j])
{
// 继续同步向后匹配
i++;
j++;
}
else
{
i = i - j + 1; // 主串字符的位置指针回退,并且主串匹配前进一个位置
j = 0; // 子串从头开始
}
}
if (j >= tLen)
{ // 一直匹配到字串尾 => 假如主串中有多个模式串,则匹配出的是最后边模式串的位置
return i-tLen; // 使主串回退到匹配的起点
}
return -1; // 匹配失败
}
void Get_next(char *p, int next[])
{
/**
* 求模式串p的next函数值,并存入数组next
*/
int i, j, pLen;
pLen = strlen(p);
i = 0;
next[0] = -1; // 若数组没有分配空间,则无法赋值
j = -1;
while (i < pLen)
{
if (j == -1 || p[i] == p[j])
{
++i; // i是一直都在往后走,和next部分匹配值表同步
++j;
next[i] = j; // 相当于存入有多少个相等的前缀后缀
}
else
{
j = next[j]; // 相当于把之前存入有多少个相等的前缀后缀存至j
}
printf("i: %d, j: %d\n",i , j);
}
/* next数组测试
printf("next: ");
for (int k = 0; k < pLen; ++k)
{
printf("%d ", next[k]);
}
printf("\n");
*/
}
int Index_KMP(char *s, char *p, int pos, int next[])
{
/**
* 利用模式串p的next函数,求p在主串中从第pos个字符开始的位置
* 若匹配成功,返回模式串在主串中的位置(下标),否则返回-1
*/
int i, j, sLen, pLen;
i = pos-1;
j = -1;
sLen = strlen(s);
pLen = strlen(p);
Get_next(p, next); // 对于数组来说,变量名即为数组首地址 - KMP算法的核心
while (i < sLen && j < pLen)
{
if (j == -1 || s[i] == p[j])
{
++i;
++j;
}
else
{
j = next[j]; // j和前面有几个相等前后缀(连续的),就到其对应的下一个位置去继续匹配
}
}
if (j >= pLen)
{
return i - pLen;
}
else
{
return -1;
}
}