Java中的字符串详解(KMP算法)

String字符串

字符串是由char类型的数组组成的

代码的执行过程

内存空间可以分为栈内存和堆内存

1..java通过javac变成.class文件,通过java命令将文件加载到方法区当中

2.首先加载main方法,会将main加载到栈内存当中,两个变量也会进行加载,栈内存中存放变量名。

字符串会被存储在字符串常量池中

因此“123”存储在字符串常量池中

3.a记录字符串的地址

字符串常量池中字符串是共享的,从而减少存储字符串内存空间,解决大量的字符冗余的问题。

4.因此b记录字符串“123”的地址,a和b记录的地址值相同,因此执行结果为true

==和equals的区别与联系

例1

public class Demo {

    public static void main(String[] args) {
        String a="123";//创建字符串
        String b="123";
        String c=new String("123");//new关键字是在堆内存当中开辟一块内存空间
        System.out.println(a==c);
        System.out.println(b==c);

        //==对于基本类型来说,==是进行值的判断
        //对于引用类型(如String 是一个类)来说,==是判断地址是否相同
    }
}

代码执行过程:

执行String c=new String("123");

会在堆内存当中新开辟一段内存空间,c会指向这块内存空间,因为c定义的值是“123”,所以这块内存空间会记录字符串常量池当中“123”的地址值。

例2

public class Demo {

    public static void main(String[] args) {
        String a="123";//创建字符串
        String b="123";
        String c=new String("123");//new关键字是在堆内存当中开辟一块内存空间
        System.out.println(a==c);
        System.out.println(b==c);
        System.out.println(a.equals(c));
        
        int a1=10;
        int b1=10;
        //System.out.println(a1.equals(b1));//equals方法报错,因为基本数据类型没有这个方法

        //==对于基本类型来说,==是进行值的判断,基本数据来行只能用==来进行判断
        //对于引用类型(如String 是一个类)来说,==是判断地址是否相同
        
        //equals首先比较地址是否相等,如果相等返回true,如果不相等,比较值是否相等
    }
}

==对于基本类型来说,==是进行值的判断,基本数据来行只能用==来进行判断
 对于引用类型(如String 是一个类)来说,==是判断地址是否相同

equals首先比较地址是否相等,如果相等返回true,如果不相等,比较值是否相等

字符串中的方法

public class Demo {

    public static void main(String[] args) {
        String a="Hello World";//创建字符串
        //length()方法------获取字符串的长度
        int length=a.length();
        System.out.println(length);
        //charAt(index)根据位置获取指定的字符,下标从0开始
        char ch=a.charAt(1);
        System.out.println(ch);
        //substring(index)方法,截取字符串
        String b=a.substring(6);//左闭区间
        System.out.println(b);
        String c=a.substring(6,11);//左闭右开区间
        System.out.println(c);
        //判断是否有子串 contains(子串)
        System.out.println(a.contains("Hel"));
        //子串替换 replace
        String newa=a.replace('o','x');
        System.out.println(newa);

        String newb=a.replace("World","aaa");
        System.out.println(newb);
        //字符串分割
        String str="apple,banana,orange";
        String[] parts=str.split(",");
        for (int i=0;i<parts.length;i++){
            System.out.println(parts[i]);
        }

       }
}

KMP算法-----大串当中找小串

在aabaabaafa中找aabaaf

解决方案1.利用双层for循环找 时间复杂度O(n^2)

解决方案2.KMP:找最长相等的前后缀组成的前缀表 O(m+n)

1.前缀和后缀

前缀:不包含最后一个字符的所有以第一个字符开头的连续字符串

后缀:不包含第一个字符的所有以最后一个字符结尾的连续字符串

2.找最长相等的前后缀组成的前缀表

3.KMP算法执行过程
第一步:

两个游标从前向后进行变量,并且比较值是否相等,如果相等执行i++j++。

如果不相等,那么则和暴力解法不—致!

第二步:

如果我们此时能够让i游标不动,同时j游标指向b是最好的。

如何指向b?

1.找前缀,后缀

2.找到最长相等前后缀的长度

3.利用前缀表重新匹配并构造next数组

很多KMP算法的实现都是使用next数组来做回退操作,那么next数组与前缀表有什么关系呢?

next数组就可以是前缀表,但是很多实现都是把前缀表统一减一(右移一位,初始位置为-1)之后作为next数组。

这并不涉及到KMP的原理,而是具体实现,next数组既可以就是前缀表,也可以是前缀表统一减一(右移一位,初始位置为-1)

next数组的构建过程(参考LeeCode有关作者)

第三步:

首先匹配串会检查之前已经匹配成功的部分中里是否存在相同的「前缀」和「后缀」。如果存在,则跳转到「前缀」的下一个位置继续往下匹配:

跳转到下一匹配位置后,尝试匹配,发现两个指针的字符对不上,并且此时匹配串指针前面不存在相同的「前缀」和「后缀」,这时候只能回到匹配串的起始位置重新开始:

代码实现:
class Solution {
    public int strStr(String haystack, String needle) {
    int n = haystack.length();
    int m = needle.length();
    
        if (m == 0) {
            return 0;
        }
        // 原串和匹配串前面都加空格,使其下标从 1 开始
       

        char[] s = haystack.toCharArray();
        char[] p = needle.toCharArray();

        // 构建 next 数组,数组长度为匹配串的长度(next 数组是和匹配串相关的)
        int[] next = new int[m];
        int j = 0;
        // 构造过程 i = 1,j = 0 开始
        for (int i = 1; i < m; i++) {
            // 匹配不成功的话,j = next(j-1)
            while (j > 0 && p[i] != p[j]){
                if(j>0){
                   j = next[j-1];
                }
                 
            }
            // 匹配成功的话, j++
            if (p[i] == p[j]){
                 
                 j++;
            }
            next[i]=j;
            
            
        }
        j=0;
        // 匹配过程
        for (int i = 0; i <n; i++) {
            // 匹配不成功
            while (j > 0 && s[i] != p[j]){
               if (j > 0) {
                j = next[j - 1];
            }
            }
            // 匹配成功的话,先让 j++,结束本次循环后 i++
            if (s[i] == p[j]){
               j++;
            } 
            // 整一段匹配成功,直接返回下标
            if (j == m) {
                return i- m+1;
                }
        }

        return -1;


}
}

字符串的拼接

示例

public static void main(String[] args) {
        //字符串的拼接
//        String s1="123";
//        String s2="456";
//        String s3=s1+s2;//
//        System.out.println(s3);
//        String s4="123456";
//        System.out.println(s3==s4);
       String s1="123"+"456";//自动拼接,在编译期间变为。class文件就会变为“123456”
          String s2="123456";
        System.out.println(s1==s2);
       }

在使用append方法进行字符串拼接之后,再通过toString()方法开辟一块新的堆内存空间,使得S3记录的地址值为新的内存空间,结果为false

在转化为.class文件时,就已经实现自动拼接,因此最后地址值相同,结果为true

字符串的输入输出

Scanner in = new Scanner(System.in);
String name = in.next();

public static void main(String[] args) {
    Scanner inScanner=new Scanner(System.in);
    System.out.println("您的姓名?");
    String name=inScanner.nextLine();
    Scanner inage=new Scanner(System.in);
    System.out.println("您的年龄?");
    int age=inage.nextInt();
    System.out.println(name+":"+age);
   }

public static void main(String[] args) {
    System.out.println("输入数组大小为:");
    Scanner inlength = new Scanner(System.in);
    int length = inlength.nextInt();

    int[] arr=new int[length];
    System.out.println("输入数组内容:");
    for(int i=0;i<arr.length;i++){
        Scanner inScanner=new Scanner(System.in);
        arr[i]=inScanner.nextInt();
    }
    System.out.println(Arrays.toString(arr));
}

执行结果

示例:一个有趣的猜一猜小游戏

public static void main(String[] args) {
 //1.通过Random类中方法nextInt(),生成一个1-100之间的随机数
 int randomNum=new Random().nextInt(100);
    System.out.println("随机数已生成");
    //2.输入猜的数字
    System.out.println("猜猜是哪个数:");
    Scanner s=new Scanner(System.in);
    int enterNum=s.nextInt();
       //3.通过while循环,进行猜数字对错判断
//猜对,跳出循环,游戏结束
    while(enterNum!=randomNum){
        //猜错了,根据结果,给出提示,接着猜数字,游戏继续            
        if(enterNum>randomNum){
            //如果猜大了,打印sorry,您猜大了!继续下一次循环
            System.out.println("猜大了");
        }else{
            //如果猜小了,打印sorry,您猜小了!继续下一次循环
            System.out.println("猜小了");
        }
        //输入猜的数字
        System.out.println("猜猜是哪个数");
        enterNum=s.nextInt();
    }
    System.out.println("恭喜你,答对了");
}

执行结果

欢迎大家点赞,评论,收藏和关注呦!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值