LeetCode之最长重复子串与无重复最长子串

leetcode-1044 最长重复子串

题干
给出一个字符串 S,考虑其所有重复子串(S 的连续子串,出现两次或多次,可能会有重叠)。
返回任何具有最长可能长度的重复子串。(如果 S 不含重复子串,那么答案为 “”。)
举个例子

输入:"banana"
输出:"ana"
输入: "dfgdsfsfdfgj"
输出:  "dfg"

思路:
首先想到的是循环,就是如何让相等的地方停下来,觉得稍微复杂一点的就需要思考一番。然后思路是 先用一个外层的循环,控制内层循环的对于外层循环的加数,这样就可以 对于循环而言 先控制加一 然后一直控制加到前一个和后一个是相等的情况出现,这个时候进入记录下来记录当前的值,然后比较大小得出最后的结果。
讲真的第一天没有写出来,想了好久如何实现,但就是有些地方没能够顺下来,就第二天时候,感觉差不多了,加上看了一个大佬的答案,觉得和我思路很相似,就记录了下来。
此代码原地址

public static String maxString(String str) {
        if (str==null||str.length()==0) {
            return "";
        }
// 记录一些初值 max表示最大相差多少,first表示对于找到记录以后开始进行输出的位置初始地方。
        int k=0;
        int max=0;
        int first=0;

        for (int i = 1; i < str.length(); i++) {
            //从当前位置开始,将相隔i 的重复的求出来!
            for (int j = 0; j < str.length()-i; j++) {
                if (str.charAt(j)==str.charAt(i+j)) {
                    k++;
                   
                }else {
                    k=0;
                }
                if (k>max) {
                    max=k;
                    first=j-k+1;//实质为:j-(k-1)
                }
            }
            k=0;
        }
        return str.substring(first, first+max);
    }

上面是第一个思路
还有一个的思路是说用前缀码来进行表示 例如给出的第一个例子,我们给出前缀是

banana,anana,nana,ana,na,a

然后进行一个排序

a ana anana  banana na nana

然后比较相邻的前缀

a  ana "" "" na 

中间的两个表示的是空,表示的是说没有前缀进行对应的匹配对象。
所以最长的是 ana 。
至于这个方法为什么能够找出来最长重复呢 就是说 第一步来说 。若是说我们拥有两个字符串A和B其前缀就是最大值。这个时候 进行一个排序的时候 这两个一定是邻接在一起的,因为既然是前缀相同才能找到最长的那个,既然前缀相同了那么在排序的时候也就必然是邻接在一起的。
若最长重复子串(假设长L)在A、B两个后缀字符串里,但是排序后AB不相邻,则AB中间的字符串C必有A[0:L-1]=B[0:L-1]=C[0:L-1],不可能有A[L]=C[L],否则和最长长度为L矛盾,则依然有相邻的A、C包含最长重复子串。
但是有一个弊端就是说,对于字符串很长的时候,就会导致存放前缀字符串的那个数组占用的空间会很大,占用的内存也会很多。
此代码原地址

 public static String longestDupSubstring(String S) {
        int len = S.length();
        String result = "";
        int maxLen = 0;
        if(len <= 1)
            return "";
        /**
         *  yedhfhyed      1
         *  edhfhyed       2
         *  dhfhyed        3
         *  hfhyed         4
         *  fhyed          5
         *  hyed           6
         *  yed            7
         *  ed             8
         *  d              9
         *
         排序以后的顺序
            93 82 546 71
         */
        String[] strs = new String[len];  // 存放S的后缀字符串
        for(int i = 0; i < len; i++){
            strs[i] = S.substring(i, len);
        }
        Arrays.sort(strs);  // 进行排序
        for(int i = 0; i < len-1; i++){  // 两个相邻字符串的最长公共前缀
            int tmp = lenTwoStr(strs[i], strs[i+1]);
            if(tmp > maxLen){
                maxLen = tmp;
                result = strs[i].substring(0,maxLen);
            }
        }
        return result;
    }
    // 两个后缀子串的前缀最长公共子串
    public static int lenTwoStr(String str1, String str2){
        if(str1.length() == 0 || str2.length() == 0)
            return 0;
        int i = 0;
        while(i < str1.length() && i < str2.length() && str1.charAt(i) == str2.charAt(i))
            i ++;
        return i;
    }

LeetCode-3 无重复的最长子串

题干
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入: "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3

示例 2:

输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1

思路:

暴力法:

就是使用暴力法对于for循环来循环判断,给定字符串里面新加入的不在我们的字符里面,就标志加一,不然标志归于初值,再度开始新的循环。只不过开始时候我们这样来写的

if(s.charAt(i)==s.charAt(j)){
// 逻辑语句
}

但是后来发现一点就是说这样的情况,就会出现若是我们已经有abc,但是后面来了一个b,这个时候就不能进行很好的判断,所以只能使用下面的这种方法。

if(s.substring(i,j).contains(s.substring(j,j+1))){
// 逻辑语句 
// 注意: substring是前闭后开的。
//前闭后开: [ )
}

代码如下:

private  static int Langest(String s){
        int num=1;
        int temp=0;
        int q=0;
        int len=s.length();
        for(int i=0;i<len;i++){
            for(int j=i+1;j<len;j++){
                if(s.substring(i,j).contains(s.substring(j,j+1))){
                        break;
                }
              else
                {
                    q=1;
                  num++;
                    if(num>temp)
                     temp=num;
                }
                }
            num=1;
            }
        if(q==0)
            return  1;
        return  temp;
    }

由于使用到了两层的for循环,所以时间复杂度为 O(N)的平方。也不是很推荐。

滑动窗口

想到这个滑动窗口,就想到了对于LeetCode的第一题也是类似的滑动窗口来解决。

代码如下:

private  static int Langest(String s){
        int len=s.length();
        Map<Character,Integer> map=new HashMap<>();
        int temp=0;
        int max=0;
        // abcabcbb
        for(int i=0;i<len;i++){
            if(map.containsKey(s.charAt(i))){
            
                temp=Math.max(temp,map.get(s.charAt(i))+1);
                // 至于这里比较大小就比较大小呗,为什么还要进行加一的操作呢。因为我们在进行put的时候加入的是下标值 而不是相对的位置,所以加1对应到位置的下一位,表重复的数值就不再会被计算到。
            }
            map.put(s.charAt(i),i);
            max=Math.max(max,i-(temp-1));
     // 上面的temp可以认为是最左边的地方,在上面加上1得出的temp,在相对位置进行操作的时候自然要进行减一的操作。
     }
        return  max;

以上就是对于字符串的两个算法题目的自我讲解,也算是LeetCode的开篇之作,下面会详解出《键值Offer》和《LeetCode经典编程题目》(牛客网)。也算是鞭策自己对于写完的题目强迫自己进行记录,你自己弄懂不算是什么,给他人讲懂才算是真正掌握。

发布了26 篇原创文章 · 获赞 5 · 访问量 705

猜你喜欢

转载自blog.csdn.net/weixin_44015043/article/details/105318196