数据结构 -- 串

基本定义

  • 由0个或者多个字符组成的有限序列

  • 串中的字符长度称为串长

  • 串中任意连续个字符组成的序列叫做该串的子串

  • 注意区分空串和空格串

串的顺序存储结构

即用一段地址连续的存储单元来存储串中的字符序列

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define Maxsize 256
//串的结构定义
typedef struct {
	char ch[Maxsize];
	int length;			//串的实际长度
}SString;
void InitString(SString *a) {
	a->length = 0;
	a->ch[0] = '\0';
}
int Strassign(SString* s, const char* str) {
	if (strlen(str) > Maxsize) {
		printf("赋值失败,超过最大串长");
		return 0;
	}
	strcpy(s->ch + 1, str); //vs2022中可能不安全,按照要求更换
	s->length = strlen(str);
	return 1;
}
int Strlength(SString s) {
	return s.length;
}
int Concat(SString* s, SString a, SString b) {
	if (a.length + b.length > Maxsize) {
		printf("超过长度限制");
		return 0;
	}
	strcpy(s->ch + 1, a.ch + 1);
	strcpy(s->ch + 1 + a.length, b.ch + 1);
	return 1;
}
int Index(SString a, SString b, int pos) {
	int i = pos;
	while (i <= a.length - b.length) {
		if (strncmp(a.ch + i, b.ch + 1, b.length)) {
			return i;
		}
		i++;
	}
	return 0;
}
int Strcompare(SString a, SString b) {
	return strncmp(a.ch + 1, b.ch + 1, a.length > b.length ? b.length : a.length);
}
int main() {
	SString S1, S2, S3;
	InitString(&S1);
	InitString(&S2);
	InitString(&S3);

	Strassign(&S1, "abcdef");
	Strassign(&S2, "def");

	printf("S1的长度:%d\n", Strlength(S1));
	Concat(&S3, S1, S2);
	printf("连接后的S3:%s\n", S3.ch + 1);

	int pos = Index(S1, S2, 1);
	if (pos) {
		printf("S2在S1中的位置:%d\n", pos);
	}
	else {
		printf("S2不是S1的子串\n");
	}

	int cmp = Strcompare(S1, S2);
	if (cmp > 0) {
		printf("S1 > S2\n");
	}
	else if (cmp < 0) {
		printf("S1 < S2\n");
	}
	else {
		printf("S1 = S2\n");
	}

	return 0;
}

串的链式存储结构

如果简单的应用链表存储字符序列,一个结点对应一个字符,这样每个结点的空间利用率低。因此一个结点可以存放一个字符,也可以存放多个字符,若是最后一个结点未被占满,可以考虑用其他字符如“#”填充

  • 串的链式存储结构除了在连接串和串操作时有一定方便,总的来说不如顺粗存储灵活,性能也不如顺序存储结构好,因此这里不给出代码

模块匹配算法

BF算法(朴素的模块匹配算法)

简单来讲,就是暴力搜索,一个个匹配下去,直到找到匹配的为止

这里的代码是直接以数组为例写的

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define Maxsize 256
void bf(char* text, char* pattern) {
    int textlen = strlen(text);
    int patternlen = strlen(pattern);
    int i = 0, j = 0;
    while (i <= textlen  && j < patternlen) {
        if (text[i] == pattern[j]) {
            i++; j++;
        }
        else {
            i = i - j + 1;        //回溯到最开始匹配的下一位
            j = 0;                //重新开始
        }
    }
    if (j == patternlen) {
        printf("匹配到的位置为:%d\n", i - patternlen + 1);
    }
    else {
        printf("匹配失败");
    }
}
int main() {
    char a[Maxsize], b[Maxsize];
    printf("请输入主串:");
    scanf_s("%s", a, Maxsize);
    printf("请输入模式串:");
    scanf_s("%s", b, Maxsize);
    bf(a, b);
    system("pause");
    return 0;
}

KMP模式匹配算法

  • KMP算法的核心思路是通过预处理模式串,构建一个部分匹配表(Partial Match Table,简称PMT),也称为最长前缀后缀数组(Longest Prefix Suffix,简称LPS)。这个表用于在匹配过程中,当出现不匹配的情况时,能够快速确定模式串的下一个匹配位置,从而避免不必要的回溯比较。

  • 为此定义了next[j]函数,表明当模式中第j个字符与主串中的相应字符“失配“时,在模式中需要重新和主串中该字符进行比较的字符的位置

  • 利用部分匹配的结果加快模式快的滑动速度,且主串的指针i不必回溯,可提速到O(n+m)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Maxsize 256

typedef struct {
    char ch[Maxsize];
    int length;
} SString;

void get_next(SString s, int *next) {
    int i = 1, k = 0;
    next[1] = 0;
    while (i < s.length) {
        if (k == 0 || s.ch[i] == s.ch[k]) {
            ++i;
            ++k;
            next[i] = k;
        } else {
            k = next[k];
        }
    }
}

void kmp(SString text, SString pattern) {
    int textlen = text.length;
    int patternlen = pattern.length;
    int i = 1, j = 1;
    int next[Maxsize];  // 确保 next 数组的大小为 Maxsize
    get_next(pattern, next);
    while (i <= textlen && j <= patternlen) {
        if (j == 0 || text.ch[i] == pattern.ch[j]) {
            i++;
            j++;
        } else {
            j = next[j];
        }
    }
    if (j > patternlen) {
        printf("匹配到的位置为:%d\n", i - patternlen);
    } else {
        printf("匹配失败\n");
    }
}

int main() {
    char a[Maxsize], b[Maxsize];
    printf("请输入主串: ");
    scanf("%s", a);
    printf("请输入模式串: ");
    scanf("%s", b);

    SString text, pattern;
    strcpy(text.ch + 1, a);  // 从下标 1 开始复制
    strcpy(pattern.ch + 1, b);  // 从下标 1 开始复制
    text.length = strlen(a);
    pattern.length = strlen(b);
    kmp(text, pattern);
    system("pause");
    return 0;
}

kmp算法的进一步改进:

当模式串为一些特殊的字符如aaaa时,当前字符不匹配时,需要回溯到 next[k],但有时 next[k] 也可能是不匹配的。因此,需要继续回溯,直到找到一个匹配的前缀或 k 为 0。

void get_nextval(SString s, int *nextval) {
    int i = 1, k = 0;
    nextval[1] = 0;
    while (i < s.length) {
        if (k == 0 || s.ch[i] == s.ch[k]) {
            ++i;
            ++k;
            if(s.ch[i]!=s.ch[k])
                nextval[i]=k;
            else
                nextval[i]=nextval[k];
        } else {
            k = nextval[k];
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值