深入理解JAVA字符串以及KMP算法深度详解

1.字符串抽象数据类型

public class test3  {
    int length()//返回串长度
    char charAt(int i )//返回第i个字符
    void setCharAt(int i,char ch)//返回第i个字符为ch
    substring(int begin,int end)//返回序号begin至end-1的子串
    concat(String s) //返回this与s串连接生成的串
    String delete(int begin,int end)//删除从begin到end-1的子串
    boolean equals(Object obj)//比较this和obj引用是否相等
    int compareTo(String s)//比较this与s串的大小,返回两者的差值
    int indexOf(String pattern)//返回首个与模式串pattern匹配的子串序号
    void removeAll(String partern)//删除所有与pattern匹配的子串
}

2.字符串和整数相互转换的方法

    //将整数转化成字符串
    int number =123;
    String numberAsString=String.valueOf(number);
    String numberAsString2=Integer.toString(number);

一般都是使用valueOf方法来进行整数转换,应为valueOf可以接收多种类型的参数,比如(int,long,float,double )等,但是toString只能接收int类型的参数。

    //把String转换成int型
    String s ="123";
    int i =Integer.parseInt(s);

3.字符串的顺序存储结构和实现

1.常量字符串

JAVA字符串类主要有String常量字符串类,StringBuffer变量字符串类等。这两种字符串类都采用顺序存储结构,能够存储任意长度的字符串,实现串的基本操作,并且能够识别序号越界等错误。

Strng字符串是一个类,属于引用数据类型。提供构造串对象,求串长度,取字符,求子串,连接串,比较相等,比较大小等操作。若charAt(i),subString(begin,end)方法参数的序号越界,则排除StringIndexOutOfBoundException字符串序号越界异常。

2.String类的特点

  1. String类是最终类,不能被继承
  2. String类是以常量串方式存储和实现字符串操作
  3. 声明字符数组是最终变量,串中各字符是只读的。当构造串对象时,对字符数组进行一次赋值,其后不能更改。String类只提供了取字符操作charAt(i),不提供修改字符,插入串和删除子串操作。
  4. 构造串,求子串和连接串的操作都是深拷贝,重新申请字符串占用的字符数组,

3.比较串大小

String类声明的compareTo()方法比较两个字符串大小,返回两者之间的差值,分别为以下三种情况:

1.若两字符串s1、s2相等,则s1.compareTo(s2)返回0。

2.若两字符串s1、s2不等,则从头开始依次将两串中的对应字符进行比较,当首次遇到两个不同字符时,s1.compareTo(s2)返回这两个不同字符的差值,

s1.charAt(i)-s2.charAt();//i为首次遇到两个不同字符的位置

3.两个字符串s1和s2,若s1是s2的前缀子串,或s2是s1的前缀子串,则s1.compareTo(s2)返回两者长度的差值

例如,"abcde".compareTo("ab")返回3。

实现compareTo()方法如下,比较两个字符串的大小,返回两者之间的差值。

public int compareTo(MyString s){
for(int i =0;i<this.value&&i<s.value.length;++){
    if(this.value[i]!=s.value[i])
         return this.value[i]-s.value[i];
     return this.values.length-s.value.length;
}
}

4.使用String串

使用String串的求子串和连接串操作,实现串的插入,删除功能。

1.插入串

设有String串s1.s2,在s1的i位置插入s2串,返回插入后的串,调用语句如下

String s1 ="a",s2 ="xyz";
int i =3;
String s3 =s1.substring(0,i)+s2+s1.substring(i);
2.删除子串

设有串s,删除串s中序号从begin到end-1子串,返回删除后的子串,调用语句如下:

int begin=3, end =6;
String s4 =s.substring(0,begin)+s.substring(end);
3.反转字符串的reverse的操作:
public class ReverseString {
    public static String reverse(String str) {
        if (str == null || str.isEmpty()) {
            return str;
        }
        return reverse(str.substring(1)) + str.charAt(0);
    }

    public static void main(String[] args) {
        String originalString = "Hello, World!";
        String reversedString = reverse(originalString);
        System.out.println("Original String: " + originalString);
        System.out.println("Reversed String: " + reversedString);
    }
}
//输出:
Original String: Hello, World!
Reversed String: !dlroW ,olleH

这就是使用递归实现的 reverse 方法。它以一种简洁的方式反转字符串,尽管这种方法在处理长字符串时可能会导致性能问题。对于长字符串,使用 StringBuilder 或 StringBuffer 通常更为高效

public class ReverseString {
    public static void main(String[] args) {
        String originalString = "Hello, World!";
        String reversedString = new StringBuilder(originalString).reverse().toString();
        System.out.println("Original String: " + originalString);
        System.out.println("Reversed String: " + reversedString);
    }
}

字符的模式串匹配

BF模式算法匹配模式

设有两个串:目标串target和模式串pattern,在target目标串中查找与pattern模式串相等的一个子串并确定改子串位置的操作称为串的模式匹配。两个子串相等是指,各对应字符相同且长度相同。匹配结果有两种:如果target中存在与pattern相等的子串,则匹配成功,获得该匹配子串在target中的位置,否则匹配失败。

KMP模式匹配算法

KMP算法,全称Knuth-Morris-Pratt字符串搜索算法,是一种高效的字符串搜索算法。它由Donald Knuth、Vaughan Pratt和James H. Morris在1977年共同发明。KMP算法的核心思想是利用部分匹配表(也称为前缀函数或部分匹配表)来避免在文本中重复搜索已经匹配过的字符。

本篇以主串为ababcabcacbab,模式串为abcac为例

先了解一些基本概念:

  1. 前缀:除最后一个字符意外,字符串的所有头部子串。
  2. 后缀:除第一个字符以外,字符串的所有的尾部子串。

目的:需要找到的是每个子串前缀和后缀相等的最长的前缀和后缀长度。

下面是一个例子:

子串前缀后缀
a
abab
abcab,abc,c
abcaabc,ab,abca,ca,a
abcacabca,abc,ab,abcac,caca,ac,c

所以,字符串abcac的最长相等前后端长度是00010,将这个长度写成数组形式,得到对应的部分匹配值[0,0,0,1,0]

模拟匹配过程

我们将模拟abcac和主串ababcabcacba进行匹配

第一趟:

主串指针len=2,模式串指针i=2时,模式串的c和主串的a匹配失败。已经匹配的字符串是ab,查看前缀表,prefix[1]=0,说明ab前缀和后缀没有相等的,所以下一趟模式串要退回到第一个字符重新比较。

下标01234567891011
主串ababcabcacba
模式串abc

第二趟:

在第二次匹配之前,子串匹配索引j直接跳到第一匹配的相同前缀串的最长匹配长度的索引位置上即j=2,主串指针len=6,模式串指针i=4时,模式串的c和主串b匹配失败。已经匹配的字符串是abca前缀和后缀有一个字符相等,所以下一趟模式串要退回到第二个字符开始重新比较,也就是退回到模式串下标为1的位置

下标01234567891011
主串ababcabcacba
模式串abcac

第三趟:

主串指针len=6,模式串指针i=0时,模式串的a和主串b匹配失败。查看前缀表,prefix[0]=0,说明前缀和后缀没有相等的,因为当前与主串比较的就是模式串的第一个字符,所以,将主串移动到下一个位置,与模式串的第一个字符进行比较。

下标01234567891011
主串ababcabcacba
模式串abcac

模式串全部比较完成,匹配成功,整个匹配过程中,主串始终没有回退,所以KMP算法的时间复杂度是O(m+n)。

next数组

在KMP算法中,next数组用于记录模式串中每个位子的最长相同的前缀和后缀的长度

next数组计算可以避免在搜索过程中的无效比较,因为如果当前字符不匹配,算法可以利用next数组直接跳到下一个可能匹配位置,而不是回到文本的最早位置。那么next是怎么计算的呢?

以abcabc为例:

j012345
模式串abcabc
最长相同前后子串长度-100012

 用abcac为讲解prefix与next之间的关系:

下标01234
字符串abc

a

c
前缀表00010
next-10001

将前缀整体右移一位,然后将空出来的第一位用-1补充,就得到了next数组。

这样子,当模式串和主串匹配失败的时候,直接查看当前匹配失败的字符的前缀表就可以了,而不是查看匹配失败字符前一个字符的前缀表了。

还是以字符串abcac与主串ababcabcacba匹配为例:

当第一趟匹配失败的时候,主串指针len=2,模式串指针i=2时,模式串的c和主串a匹配失败。查看前缀表,prefix(2)=0,说明ab前缀和后缀没有相等的,所以下一趟模式串要退回到第一个字符重新比较,也就是退到模式串pattern的下标0的位置

下标01234567891011
主串ababcabcacba
模式串abc

这里是从-1开始存储和计算next数组的没所以此时next数组的含义是指:当模式串的第i个字符与主串发生匹配失败时,就退回到模式串的next[i]重新与主串进行匹配。当然可以从0开始存储和计算next数组的。

下标01234
字符串abc

a

c
next01112
 KMP算法的代码实现
public class KMPAlgorithm {
    private int[] computeTemporaryArray(char pattern[]){
        int[] lps =new int[pattern.length];//创建一个名为lps的整数数组,其长度与pattern数组相同。这个数组将用于存储最长公共前后缀的长度
        int index =0;//用于在lps数组中跟踪当前的最长公共前后缀的长度。
        for (int i =1;i<pattern.length;i++){//开始一个循环,从数组的第二个元素开始(索引为1),直到数组的末尾。
            if (pattern[i]==pattern[index]){
                lps[i]=index+1;
                index++;
                i++;
            }else{
                if (index!=0){
                    index =lps[index-1];
                }else{
                    lps[i]=0;
                    i++;
                }
            }
        }
        return lps;
    }
    public boolean KMP(char[] text,char[] pattern){
        int[] lps =computeTemporaryArray(pattern);
        int i =0;
        int j =0;
        while (i<text.length&&j<pattern.length){
            if (text[i]==pattern[i]){
                i++;
                j++;
            }
        }
        if (j==pattern.length){
            return true;
        }else if(i<text.length&&text[i]!=pattern[j]){
            if (j!=0){
                j=lps[j-1];
            }else{
                i=i+1;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        KMPAlgorithm kmp =new KMPAlgorithm();
        String txt ="ABABDABACDABABCABAB";
        String pat="ABABCABAB";
        char[] text =txt.toCharArray();
        char[] pattern=pat.toCharArray();
        boolean result =kmp.KMP(text,pattern);
        System.out.println("The pattern is found in the text:"+result);
    }

  • 34
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值