代码随想录刷题Day11

107 阅读4分钟

字符串

  1. LCR 182. 动态口令
  • 方法一:subtring一行解决
class Solution {
    public String dynamicPassword(String password, int target) {
        return password.substring(target)+password.substring(0,target);
    }
}
  • 方法二:局部反转+整体反转
  1. 反转区间为前n的子串
  2. 反转区间为n到末尾的子串
  3. 反转整个字符串
class Solution {
    public String dynamicPassword(String password, int target) {
        int len=password.length();
        StringBuilder sb=new StringBuilder(password);
        reverseString(sb,0,target-1);
        reverseString(sb,target,len-1);
        return sb.reverse().toString();
    }
     public void reverseString(StringBuilder sb, int start, int end) {
        while (start < end) {
            char temp = sb.charAt(start);
            sb.setCharAt(start, sb.charAt(end));//反转
            sb.setCharAt(end, temp);
            start++;
            end--;
            }
        }
}
  1. 28. 找出字符串中第一个匹配项的下标
  • 方法一:暴力

只要needle开头的字母与haystack的一个字母匹配,马上进入while循环查询是否匹配,如果不匹配,重新开始下一次循环

class Solution {
    public int strStr(String haystack, String needle) {
        char[] h = haystack.toCharArray();
        char[] n = needle.toCharArray();
        boolean flag=false;
        for(int i=0;i+n.length<=h.length;i++){
            int j=i,index=0;
            while(h[j]==n[index]){
                j++;index++;
                if(index==n.length){
                    flag=true;
                    break;
                }
            }
            if(flag){
                return i;
            }
        }
        return -1;
    }
}

官方的暴力解法

class Solution {
    public int strStr(String haystack, String needle) {
        char[] h = haystack.toCharArray();
        char[] n = needle.toCharArray();
        for(int i=0;i+n.length<=h.length;i++){
            int flag=0;
            for(int j=0;j<n.length;j++){
                if(h[i+j]!=n[j]){
                    flag=1;
                    break;
                }
            }
            if(flag==0){
                return i;
            }
        }
        return -1;
    }
}
  • 方法2:KMP算法

上面两个图解讲的是比较清楚的,可以参考。看完了还不是特别懂。。next数组构造那一块挺难理解,有空再回来看看。

class Solution {
    public int strStr(String haystack, String needle) {
        if (needle.length() == 0) return 0;
        int[] next = new int[needle.length()];
        getNext(next, needle);
        int j = 0;
        for (int i = 0; i < haystack.length(); i++) {
            while (j > 0 && needle.charAt(j) != haystack.charAt(i)) //找到匹配不成功的位置,查找前缀表next[j-1],赋值给j。就可以跳到当前位置继续比较
                j = next[j - 1];//j前缀表[j-1]的值,
            if (needle.charAt(j) == haystack.charAt(i)) //当前字符匹配成功的话,先让j++,结束本次循环后i++
                j++;
            if (j == needle.length()) //全部匹配成功,返回下标
                return i - needle.length() + 1;
        }
        return -1;
    }
    private void getNext(int[] next, String s) {
        int left = 0;//left指向前缀末尾,也代表了最长相等前后缀
        next[0] = 0;//初始化第一位
        for (int right = 1; right < s.length(); right++) {//right指向后缀末尾
            while (left > 0 && s.charAt(left) != s.charAt(right)) //如果不相等就让left指针回退。这地方特别难理解
                left = next[left - 1];
            if (s.charAt(left) == s.charAt(right)) 
                left++;
            next[right] = left; 
        }
    }
}
  1. 459. 重复的子字符串
  • 方法一:暴力

leetcode.cn/problems/re…

先把s的所有长度<=/2的子字符串,保存到集合str

对子字符串集合进行遍历,记子字符串长度为:m = sub.length()

如果某一个子字符串不能通过重复多次组合成长度等于n的字符串,即n % m != 0,则说明s不能由这个子字符串重复组成;

如果某一子字符串可以通过重复多次组合成长度等于n的字符串,即n % m == 0, 则说明s可能由这个子字符串重复组成。① 若由子字符串组合成新的字符串内容等于s,则返回true,否则遍历下一个子字符串。

class Solution {
   public boolean repeatedSubstringPattern(String s) {
       int n=s.length();
       List<String> str = new ArrayList<>();
       for(int i=1;i<=n/2;i++){
           str.add(s.substring(0,i));
       }
       for(String sub: str){
           int m=sub.length();
           if(n%m!=0) continue;
           String sb= new String();
           for(int i=0;i<n/m;i++){
               sb+=sub;
           }
           if(s.equals(sb)){
               return true;
           }
       }
       return false;
    }
}

方法二:移动匹配

这个思路挺妙的

当一个字符串s:abcabc,内部由重复的子串组成,那么这个字符串的结构一定是这样的:

image.png

也就是由前后相同的子串组成。

那么既然前面有相同的子串,后面有相同的子串,用 s + s,这样组成的字符串中,后面的子串做前串,前面的子串做后串,就一定还能组成一个s,如图:

image.png

所以判断字符串s是否由重复子串组成,只要两个s拼接在一起,里面还出现一个s的话,就说明是由重复子串组成。

当然,我们在判断 s + s 拼接的字符串里是否出现一个s的的时候,要刨除 s + s 的首字符和尾字符,这样避免在s+s中搜索出原来的s,我们要搜索的是中间拼接出来的s。

class Solution {
   public boolean repeatedSubstringPattern(String s) {
       String str = s + s;
       str=str.substring(1,str.length()-1);//掐头去尾
       return str.contains(s);
   }
}

方法三:KMP(我晕)

真的看不懂,只能记住这个公式,len(s) % (len(s) - maxLen) = 0,其中 len(s) 为字符串 s 的长度maxLen 为最长公共前后缀的长度

这个公式翻译一下就是:如果 s 是周期串,那【s 的长度】是【s 的长度减去最长公共前后缀的长度】的倍数,那字符串 s 就是周期串

class Solution {
   public boolean repeatedSubstringPattern(String s) {
        if (s.equals("")) return false;
        int len = s.length();
        int[] next = new int[len];
        getNext(next, s);
        // 最后判断是否是重复的子字符串,这里 next[len-1] 即代表next数组末尾的值
        if (next[len-1] > 0 && len % (len - next[len-1]) == 0) {
            return true;
        }
        return false;
   }

    private void getNext(int[] next, String s) {
        int left = 0;//left指向前缀末尾,也代表了最长相等前后缀
        next[0] = 0;//初始化第一位
        for (int right = 1; right < s.length(); right++) {//right指向后缀末尾
            while (left > 0 && s.charAt(left) != s.charAt(right)) //如果不相等就让left指针回退,到0时就停止回退
                left = next[left - 1];
            if (s.charAt(left) == s.charAt(right)) 
                left++;
            next[right] = left; 
        }
    }
}