字符串——28,459

今天一共学习字符串相关的两道题,个人认为需要学习的内容不少,难度也较大,分别是LeetCode.28LeetCode.459

LeetCode.28找出字符串中第一个匹配项的下标

找出字符串中第一个匹配项的下标
本题和前面的题一样,都可以暴力破解。使用两层for循环将字符串中的每一个子串遍历出来,和待匹配项比较,返回第一次匹配成功的下标,如果遍历完字符串都没有匹配成功,则返回-1。

	public static int strStr(String haystack,String needle){
        for (int i = 0; i < haystack.length(); i++) {
            if (haystack.charAt(i)==needle.charAt(0)) {
                for (int j = i; j <haystack.length(); j++) {
                    if (haystack.substring(i,j+1).equals(needle)){
                        return i;
                    }
                }
            }
        }
        return -1;
    }

注意,该算法的时间复杂度为O(N*M),N为字符串的长度,M为待匹配串的长度。而这种类型的题目,关于在一个字符串中寻找子串,有一个专门的算法,叫做KMP算法。这个算法在学数据结构的时候就学到过,不过只学了理论,实际的代码实现并没有思考过,今天重点内容就是这个KMP算法。

这个算法一共有两步,第一步是构建next数组,第二步是按照next数组寻找子串,KMP算法的时间复杂度为O(N+M)。

第一步,构建next数组。next数组有三种实现方式,第一种为原始实现,第二种将next[0]置成-1,并将后方原始实现的数组元素整体后移,第三种是将原始数组的next每一个元素-1。第二种元素在代码实现和理解上会有稍稍的改进,但也是需要了解最原始的实现方式,这里使用第一种也就是最原始的实现方式。这里将会直接说next的数组实现方式,具体原理可以参考印度老师的视频,我认为是讲的最清晰的,在b站上或者油管都可以搜到。首先定义一个长度和待匹配字符串长度相同的整型数组。将数组的第一个元素置为0,定义两个指针,j指向字符串首元素,i指向字符串第二个元素。如果i和j指向元素相等,那么next[i]的值为j+1,接着将j和i都向后移动一位。如果i和j指向元素不相等,并且j>0时,即在j不指向首元素且i,j指向元素不相等时,将j指向next数组种j前一位处,即j=next[j-1],如果还不相等,继续重复回溯,直到相等,同样这时next[i]=j+1。如果一直回溯到j=0,这时如果相等,那么同样next[i]=j+1,也就是这时next[i]的值为1。如果j=0所指元素仍不相等,则next[i]=0,将i后移一位,继续匹配。

	public static int[] getNext(String s){
        int[] next=new int[s.length()];
        next[0]=0;
        int j=0;
        for (int i = 1; i <s.length() ; i++) {
            while(j>0&&s.charAt(j)!=s.charAt(i)){
                j=next[j-1];
            }
            if (s.charAt(i)==s.charAt(j)){
                next[i]=j+1;
                j++;
            }else{
                next[i]=0;
            }
        }
        return next;
    }

第二步是使用next数组进行匹配两个字符串,两个字符串分别是字符串haystack和待匹配子串needle。过程和第一步很像,首先使用两个指针,i指向haystack,j指向needle。这个过程中,haystack一直向前,needle如果碰到不相等的地方将会进行回溯,而回溯的地方不再是暴力破解时回溯到起始位置,而是根据next进行部分回溯或者全部回溯。如果i指向元素和j指向元素相等,两个指针同时向后移动,如果不相同,仍然看next数组中j前面一位的值,将j回溯到该处,继续判断是否和i指向元素相等,若是相等,则j和i继续向后移动匹配,如果还不相等,继续按next数组回溯。直到将needle字符串中的每一个元素都匹配完,则认为匹配成功,返回对应下标。如果将haystack遍历完成后仍未匹配到,则认为匹配不成功,根据题目要求,返回-1。

	public static int strStrKMP(String haystack,String needle){
        int[] next=getNext(needle);
        int j=0;
        int i=0;
        for (; i < haystack.length(); i++) {
            while(j>0&&haystack.charAt(i)!=needle.charAt(j)){
                j=next[j-1];
            }
            if (haystack.charAt(i)==needle.charAt(j)){
                j++;
            }
            if(j>=needle.length()){
                return i-needle.length()+1;
            }
        }
        return -1;
    }

LeetCode.459重复的子字符串

重复的子字符串

这道题的思路是,将两个字符串s拼接成一个字符串ss,如s="abcdeabcde",那么ss="abcdeabcdeabcdeabcde"。接着将ss“掐头去尾”,将第一个元素和最后一个元素去掉,在ss中寻找是否有和s相等的子串,如果有,则认为s是由重复的子字符串构成,反之则不是。

为什么要“掐头去尾”,因为如果不掐头去尾,则以前组成ss的s也会被匹配到,寻找子串的操作可以借鉴上一题的KMP算法。

	public static int[] getNext(String s){
        int[] next=new int[s.length()];
        next[0]=0;
        for (int j=0,i=1;i<s.length();i++) {
            while(j>0&&s.charAt(i)!=s.charAt(j)){
                j=next[j-1];
            }
            if (s.charAt(i)==s.charAt(j)){
                next[i]=j+1;
                j++;
            }else{
                next[i]=0;
            }
        }
        return next;
    }

    public static boolean contain(String haystack,String needle){
        int[] next=getNext(needle);
        int j=0;
        for (int i = 0; i < haystack.length(); i++) {
            while(j>0&&haystack.charAt(i)!=needle.charAt(j)){
                j=next[j-1];
            }
            if (haystack.charAt(i)==needle.charAt(j)){
                j++;
            }
            if (j>=needle.length()){
                return true;
            }
        }
        return false;
    }


    public static boolean repeatedSubstringPattern(String s){
        StringBuilder sb=new StringBuilder(s+s);
        sb.delete(0,1);
        sb.delete(sb.length()-1,sb.length());
        return contain(sb.toString(),s);
    }
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值