文章目录

  • 1.字符串定义
  • 2.串的几个基本概念
  • 2.1 空串:
  • 2.2空格串
  • 2.3子串
  • 2.4串相等
  • 2.5串比较
  • 3.串的基本操作(`此处以java为例`)
  • 3.1赋值操作StrAssign(s,t)
  • 3.2 连接操作 Concat(s,t)
  • 3.3求串长StrLength(s)
  • 3.4比较StrCompare(st)
  • 3.5 求子串_SubString(s,start,len)
  • 4.串的存储结构
  • 4.1 串的顺序存储结构
  • 4.2 串的链式存储
  • 5.朴素的模式匹配算法
  • 6.改进的模式匹配算法(KMP算法)


1.字符串定义

字符串是一种特殊的线性表,是由字符构成的有限序列,其数据元素是字符。

2.串的几个基本概念

2.1 空串:

长度为零的串称为空串,空串不包含任何字符。

2.2空格串

由一个或多个空格组成的巾。虽然空格是一个空白字符,但它也是一个字符,在计算串长度时要将其计算在内。

2.3子串

由串中任意长度的连续字符构成的序列称为子串。含有子串的串称为主串。子串在主串中的位置是指子串首次出现时,该子串的第一个字符在主串中的位置。空审是任意串的子串。

2.4串相等

指两个串长度相等且对应序号的字符也相同。

2.5串比较

两个串比较大小时以字符的ASCI码值(或其他字符编码集合作为依据实质上,比较操作从两个串的第一个字符开始进行,字符的码值大者所在的串为大,若其中一个串先结束,则以串长较大者为大。

3.串的基本操作(此处以java为例)

java中的字符串是一个final类,实现了CharSequence接口。实际内部也是也是一个字符数组,如下截图

两个字符串之间的空格只保留一个java_java

3.1赋值操作StrAssign(s,t)

将串s的值赋给串t。

String s1 = new String("我是字符串1");
  String s2 = "我是字符串1";
  String s21 = "我是字符串1";
  System.out.println(s1==s2);//false
  System.out.println(s1.hashCode()==s2.hashCode()); //true
  System.out.println(s2==s21);//true
  String s3 = new String("我是字符串2");
  String s4= new String("我是字符串2");
  System.out.println(s3==s4); //false
  System.out.println(s3.hashCode()==s4.hashCode()); //true

3.2 连接操作 Concat(s,t)

将串t接续在串s的尾部,形成一个新串

String s1="我是字符串1";
String s2="我是字符串2";
System.out.println(s1.concat(s2));

3.3求串长StrLength(s)

返回串s的长度。

String s="我是字符串";
 System.out.println(s.length());//5

3.4比较StrCompare(st)

比较两个串的大小。返回值-1、0和1分别表示st、st和s>t 三种情况。java中返回的是正值或者负值

String s1="a";
 String s2="b";
 System.out.println(s1.compareTo(s2));//-1
 System.out.println(s2.compareTo(s1));//1

CompareTo源码

public int compareTo(String anotherString) {
        //原始字符串的长度
        int len1 = value.length;
        //比较的字符串的长度
        int len2 = anotherString.value.length;
        //比较两个字符串相对较短的一个字符串
        int lim = Math.min(len1, len2);
        //拿到字符数组
        char v1[] = value;
        char v2[] = anotherString.value;
        //从较短的第一个字符串开始一个一个对比
        int k = 0;
        while (k < lim) {
            char c1 = v1[k];
            char c2 = v2[k];
            //字符不相等直接返回
            if (c1 != c2) {
               //两个字符的相减其实对应的acsll值相减 System.out.println('a'-'c'); -2
                return c1 - c2;
            }
            k++;
        }
        return len1 - len2;
    }

3.5 求子串_SubString(s,start,len)

返回串s中从start 开始的、长度为len 的字符序列。

String str="helloword";
 System.out.println(str.substring(2,4));//ll
 System.out.println(str.substring(0,5));//hello

4.串的存储结构

串可以进行顺序存储或链式存储。

4.1 串的顺序存储结构

串的顺序存储结构是指用一组地址连续的存储单元来存储串值字符序列。由于串中的元素为字符,所以可通过程序语言提供的字符数组定义串的存储空间也可以根据串长的需要动态申请字符串的空间。

4.2 串的链式存储

当用链表存储串中的字符时,每个结点中可以存储一个字符,也可存储多个字符,此时要考虑存储密度问题。在链式存储结构中,结点大小的选择会直接影响串的处理效率

5.朴素的模式匹配算法

思路:从主串的第一个字符开始与模式串的第一个开始比较,相等则继续下一个字符串进行对比,相反则从主串的第二个字符开始与模式串的第一个字符串重新开始比 ,直到模式串的每一个都能与主串相等为止,则匹配成功。否则匹配失败。

此处以C语言为例子进行实现:

#include <stdio.h>
#include <string.h>
/**
 * /**
 * 比较字符串返回匹配成功的下标
 * @param s 主串
 * @param p  模式匹配串
 * @param pos 主串开始的位置
 * @return
 */
int Index(char s[],char p[],int pos) {
    //i代表匹配的位置 j代表模式串的位置,sLen代表主串的长度,pLen代表模式串的长度
    int i, j, sLen, pLen;
    //传入的主串的位置开始匹配
    i = pos;
    //模式串从第一个字符开始匹配
    j = 0;
    //计算主串和子串的长度
    sLen = strlen(s);
    pLen = strlen(p);
    while (i < sLen && j < pLen) {
        //如果两个字符匹配,则继续匹配
        if (s[i] == p[j]) {
            i++;
            j++;
        } else {
            //否则,匹配串的位置回到0位置,主串进行回到起始匹配的下一个位置进行比较
            i = i - j + 1;
            j = 0;
        }
    }
    //匹配完了,j的值一定是大于等于模式串的长度,返回的匹配开始的位置
    if (j >= pLen) {
        return i - pLen;
    }
    //匹配失败
    return -1;
}
int main() {
    char s[]="helloword";
    char p[]="word";
    int res = Index(s,p,0);
    printf( "%d",res);
    return 0;
}

两个字符串之间的空格只保留一个java_数据结构_02

6.改进的模式匹配算法(KMP算法)

思路: 朴素的模式匹配算法存在一个问题,时间复杂度比较大,匹配的时候都是从主串的下一个位置与模式串的第一个位置开始,最坏的情况下时间复杂度为O(mxn)。

改进的模式匹配:
匹配过程中出现比较字符不相等时候,不需要退回主串的字符串位置指针,利用结构使主串尽可能滑动可能远的距离,再继续进行比较。

KMP的算法的核心是求出Next函数的过程

当模式串中的字符Pj与主串中相应的字符Si不相等时,因为前j个字符(“P0…Pj-1”)已经匹配成功,如果模式匹配中的P0…Pk-1与Pj-k…Pj-1相同,这时可以使用Pk与Si进行比较,从而使i无需回退。

令next(j)=k,表示模式串中的Pj与主串中的字符不相等时,令模式串Pnext(j)与主串的相应的字符进行比较。Next的函数表达式:

1)当j=0时,next[j]=-1
2) 当{k| 0<k<j并且P0…Pk-1=Pj-k…Pj-1},next[j]=max
3) 其他情况,next[j]=0

第二种情况比较难以理解:目的是寻找当前j位置前的公共前后缀

/**
 * 求解模式匹配串的next数组
 * @param p  模式匹配串
 * @param next next数组
 */
void GetNext(char *p,int next[]){
    int i=0,j=-1,len;
    //模式串的长度
    len= strlen(p);
    //模式串数组的第1位肯定-1
    next[0]=-1;
    //循环求解模式串
    while(i < len){
        if(j==-1 || p[i] == p[j]){
            ++i;
            ++j;
            next[i]=j;
        }else{
            j = next[j];
        }
        printf("the %d th loop,i=%d,j=%d,next[%d]=%d",i,i,j,i,next[i]);
        printf("\n");
    }
}

测试代码:

int main() {
    char p[] = "ababa";
    int next[5] ;
    //获取next数组
    GetNext(p,next);
    for (int i = 0; i <= 5; ++i) {
        printf("%d,",next[i]);
    }
    return 0;
}

两个字符串之间的空格只保留一个java_java_03