线性结构——串

一、串的相关定义

  1. 串的定义:串是由零个或多个组成的有序序列,又叫字符串。
  2. 串的长度:串中字符的数目称为串的长度。
  3. 空串:由零个字符组成的串叫做空串,空串不包括任何字符,其长度为零。
  4. 子串:串中任意个连续的字符组成的子序列称为该串的子串,空串是任何串的子串。
  5. 主串:包含子串的串相应的称为主串。
    例:假设有两个串 a 和 b,如果 a 中可以找到几个连续字符组成的串与 b 完全相同,则称 a 是 b 的主串,b 是a 的子串。例如,若 a = “hello world”,b = “world”,由于 a 中也包含 “world”,因此串a 和串 b是主串和子串的关系;

二、串的实现

1. 定长顺序存储:实际上就是用普通数组(又称静态数组)存储

2. 堆分配存储:用动态数组存储字符串;

3. 块链存储:用链表存储字符串;

2.1 定长顺序存储

  • 串的定长顺序存储结构,可以简单地理解为采用 “固定长度的顺序存储结构” 来存储字符串,因此限定了其底层实现只能使用静态数组。
  • 使用定长顺序存储结构存储字符串时,需结合目标字符串的长度,预先申请足够大的内存空间。

我们在这里使用 定长顺序存储 存储 “hello world" 这个字符串,加上尾字符,一个11个
在这里插入图片描述

#include <stdio.h>
#include <string.h>

int main(void)
{
  char arr[11] = "hello world";
  printf ("size: %ld   string: %s\n",strlen(arr), arr);
  return 0;
}

结果:

size: 11   string: hello world

2.2 堆分配存储

使用动态数组,优势就是长度可变
在这里插入图片描述

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(void) 
{
  char *str1, *str2;
  int length1, length2;
  int i;

  str1 = (char *) malloc (11 * sizeof(char));  //str1
  strcpy (str1, "hello world");
  length1 = strlen (str1);
  printf ("str1 string:  %s\n",str1);

  str2 = (char *) malloc (5 * sizeof(char));  //str2
  strcpy (str2, " yes!");
  length2 = strlen (str2);
  printf ("str2 string: %s\n",str2);

  if (length1 < length1 + length2) {  //扩展str1
    str1 = realloc (str1, (length1 + length2 + 1) * sizeof (char));
  }

  for (i = length1; i < length1 + length2; i++) {  //合并两个字符串到str1
    str1[i] = str2[i - length1];
  }
  str1[i] = '\0';
  
  printf ("new  string:   %s\n",str1);

  free (str1);
  free (str2);

  return 0;
}

结果:

str1 string:  hello world
str2 string:  yes!
new string:   hello world yes!

2.3 链表存储

2.3.1 定义数据类型

在这里插入图片描述

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef  char datatype;

typedef struct strlist_t {
  datatype          *data;           //数据域
  struct strlist_t  *next;  //指针域
}strlist;

2.3.2 初始化串

strlist *InitStrList (datatype *data)  //初始化链串
{
  strlist *str = NULL;
  str = malloc (sizeof (strlist));
  str->next = NULL;
  str->data = malloc (sizeof (datatype) * SIZE);
  memcpy (str->data, data, sizeof (datatype) * SIZE);
  printf ("str1 : %s\n",str->data);

  return str;
}

2.3.3 尾插子串

int InsertStr (strlist *str, datatype *data)  //尾插子串
{
  strlist *list = NULL;
	strlist  *q   = NULL;

  list = malloc (sizeof (strlist));
  list->next = NULL;
  list->data = malloc (sizeof (datatype) * SIZE);
	memcpy (list->data, data, sizeof (datatype) * SIZE);

  for(q = str; q->next != NULL; q = q->next);
		q->next = list;
    q->next->next = NULL;

  printf ("add str : %s\n", list->data);

  return 0;
}

2.3.4 打印串

int displaystr (strlist *str)  //打印串
{
  strlist  *q   = NULL;
  for(q = str; q != NULL; q = q->next) {
    printf ("%s",q->data);
  }
  
  printf ("\n");
  return 0;
}

2.3.5 测试

int main (void)   //块链存储
{
  strlist *StrList = NULL;

  StrList = InitStrList ("test ");

  InsertStr (StrList, "ok!");

  displaystr (StrList);

  return 0;
}

结果:

str1 : test
add str : ok!
test ok!

三、字符串匹配

3.1 BF算法

全名 Brute-Force 是一个纯暴力算法,使用穷举依次比对,如下:
在这里插入图片描述

// str1 主串     str2 模式串
int str_mate (char *str1, char *str2)
{
  int i = 0, j = 0;

  while (i < strlen (str1) && j < strlen (str2)) {
    if (str1[i] == str2[j]) {
      printf ("匹配: %c \n", str1[i]);
      i++;
      j++;
    } else {
      printf ("不配: %c \n", str2[j]);
      i = i - j + 1;
      j = 0;
    }
  }

  if (j == strlen (str2)) {
    return i - strlen (str2) + 1;   //返回匹配的位置
  }

  return 0;
}

int main (void)  //普通模式匹配
{
  int num;
  
  num = str_mate ("abcdacba", "acb");
  printf ("第%d个位置\n", num);

  return 0;
}

测试如下
在这里插入图片描述

3.2 KMP算法

Knuth-Morris-Pratt 字符串查找算法,简称为 KMP算法,常用于在一个文本串 S 内查找一个模式串 P 的出现位置。

3.2.1 寻找前缀后缀最长公共元素长度

KMP算法的核心,是一个被称为部分匹配表(Partial Match Table)的数组,简称PMT (前缀后缀最长公共元素长度)

  • 前缀:除最后一个字符外,一个字符串的全部头部组合。例如:“Hello” 的前缀包括 { “H” , “He” , “Hel” , “Hell”};
  • 后缀:除了第一个字符外,一个字符串的全部尾部组合。例如:"OkHe"的后缀包括 {“e” , “He” , “kHe”};

所以上述前缀后缀最长公共元素长度为: “He”

假设模式串 “abaabcac”,则
在这里插入图片描述

3.2.2 Next数组

next 数组考虑的是除当前字符外的最长相同前缀后缀,相当于把PMT整体后移一位,前一位为 0 的,由 -1 替代。
在这里插入图片描述
求next数组的过程完全可以看成字符串匹配的过程,即以模式字符串为主字符串,以模式字符串的前缀为目标字符串,一旦字符串匹配成功,那么当前的next值就是匹配成功的字符串的长度。具体来说,就是从模式字符串的第一位(注意,不包括第0位)开始对自身进行匹配运算。 在任一位置,能匹配的最长长度就是当前位置的next值。

void GetNextval(char* p, int next[])  
{  
  int k;
	int i = 0, j = -1;

  next[0] = -1;

	while (i < (int)strlen(p)) {
		if (j == -1 || p[i] == p[j]) {
			++i;
			++j;
			next[i] = j;
      //printf ("i: %d %d \n",i, next[i]);
		}	else {
      //printf ("j: %d %d \n",j, next[j]);
      j = next[j];
      
    }
	}
  
  printf ("next 数组:\n");
  for (k = 0; k < strlen(p); k++) {
    printf ("[%d]:%d  ",k, next[k]);
  }

}  

3.2.3 kmp实现

在这里插入图片描述

int KmpSearch(char* s, char* p)  
{  
  int next[10];
  int i = 0;
  int j = 0;
  int sLen = strlen(s);
  int pLen = strlen(p);

  GetNextval (p, next);

  while (i < sLen && j < pLen) {
    //如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++      
    if (j == -1 || s[i] == p[j]) {
      i++;
      j++;
    } else {
       //如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]
       //next[j]即为j所对应的next值
       j = next[j];
    }
  }

  if (j == pLen)
    return i - j;
  else
    return -1;

}  

3.2.4 测试

int main(void) 
{
  int num;

  num = KmpSearch ("acabaabaabcacb", "abaabcac");  //返回匹配数组下标
  printf("\n匹配数组下标:%d\n",num); 
  return 0;
}

在这里插入图片描述
参考1
参考2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值