JAVA实现字符串子串算法问题

找到字符串最长无重复子串

题目描述

给定一个数组arr,返回arr的最长无的重复子串的长度(无重复指的是所有数字都不相同)。
输入
[2,2,3,4,3]
输出
3

方法一

滑动窗口

代码实现

import java.util.*;
public class Solution {
    /** 
     * @param arr int整型一维数组 the array
     * @return int整型
     */
    public int maxLength (int[] arr) {
        // write code here
        //左边界left
        int left = -1;
        int res = 0;
        //HashMap 存放出现过的数字以及其坐标
        HashMap<Integer, Integer> hashmap = new HashMap<>();
        //右边界right,往右扩张right++
        for(int right = 0;right < arr.length; right++){
        //判断右边界该位置的数字是否出现过
        //若出现过则将左边界移至该数字上一次出现过的位置
        //保证左右边界内数字没有重复
        //可能遇到索引比left原来还小的相同数字,加上max判断
            if (hashmap.containsKey(arr[right])){
                left = Math.max(left, hashmap.get(arr[right]));
            }
        //更新坐标
            hashmap.put(arr[right], right);
            
            res = Math.max(res, right - left);
        }
        return res;
        
    }
}

HashMap 简单介绍

HashMap 储存数据的方式是 —— 键值对
对于 HashMap<key, value>而言,key 是唯一的,不可以重复的。
所以,以相同的 key 把不同的 value 插入到 Map 中会导致旧元素被覆盖,只留下最后插入的元素。 不过,同一个对象可以作为值插入到 Map 中,只要对应的key不一样。

方法二

双指针+回头遍历

代码实现

public int maxLength (int[] arr) {
     int res = 0, tmp = 0;
    //right指针往右移动
    for(int right = 0; right < arr.length; right++){
        int left = right - 1;
        //回头扫描,要是没有找到相同的,左指针一直倒退
        while(left >= 0 && arr[right] != arr[left])
            left--;
        //暂时保存子串长度
        //若指针距离比上一个字符时拥有的子串长度大,就tmp + 1,否则就设置为指针距离,方便下一步res进行比较
        tmp = tmp < right - left ? tmp + 1 : right - left;
        res = Math.max(res,tmp);
    }
    return res;

最长公共子串

题目描述

给定两个字符串str1和str2,返回两个字符串的最长公共子串
输入
str1=“1AB2345CD”,str2=“12345EF”
输出
“2345”

方法

动态规划

代码实现

public String LCS(String str1, String str2){
    if(str1.length() == 0 || str2.length() == 0){
        return "-1";
    }
    //起始位置
    int start1 = 0;
    //偏移量
    int max = 0;
    int len1 = str1.length();
    int len2 = str2.length();
 
    int[][] dp = new int[len1+1][len2+1];
 
    for(int i = 1; i <= len1; i++){
        for(int j = 1; j <= len2; j++){
            if(str1.charAt(i-1) == str2.charAt(j-1)){
                  //将当前相等的字符计入 + 1
                  dp[i][j] = dp[i-1][j-1] + 1;
            }else {
                dp[i][j] = 0;
            }
 
            if(max < dp[i][j]){
                //max记录已经拥有的子串长度
                max = dp[i][j];
                //因为字符串索引是从0开始的,所以 i - max 刚好是起始位置
                start1 = i - max;
 
            }
        }
    }
    if(max == 0) return "-1";
    return str1.substring(start1, max+start1);
}

最长回文子串

题目描述

对于一个字符串,请设计一个高效算法,计算其中最长回文子串的长度。给定字符串A以及它的长度n,请返回最长回文子串的长度。
输入
'abc1234321ab", 12
输出
7

代码实现

方法

暴力

public class Palindrome {
    public int getLongestPalindrome(String A, int n) {
        int res = 0;
        //暴力解法
        for(int i = 0; i < n; i++){
            for(int j = i+1; j <= n; j++){
                String tmp = A.substring(i,j);
                //调用判断是否为回文子串的方法
                if(isPalindrome(tmp)){
                    res = Math.max(res, tmp.length());
                }
            }
        }
        return res;
    }

    public boolean isPalindrome(String s) {
        int len = s.length();
        for(int i=0; i< len/2; i++){
            if(s.charAt(i) != s.charAt(len-1-i))
                return false;
        }
        return true;
    }
}

最长的括号子串

题目描述

给出一个仅包含字符(‘和’)的字符串,计算最长的格式正确的括号子串的长度。对于字符串"(()"“来说,最长的格式正确的子串是”()",长度为2.
输入
“)()())”
输出
4

方法一

使用栈

代码实现

public int longestValidParentheses (String s) {
    int res = 0;
    Stack<Integer> stack = new Stack<>();
    stack.push(-1);
    for(int i=0; i < s.length(); i++){
        if(s.charAt(i) == '('){
            stack.push(i);
        } else {
            stack.pop();
            if(stack.empty()) {
                stack.push(i);
            } else {
                res = Math.max(res, i - stack.peek());
            }
        }          
    }
    return res;
}

方法二

动态规划

代码实现

public int longestValidParentheses (String s) {
    int res = 0;
    int[] dp = new int[s.length()];
    for(int i = 1; i < s.length(); i++){
        //只对当前字符是')'进行处理
        if(s.charAt(i) == ')'){
            //当()时
             if(s.charAt(i-1) == '('){
                dp[i] = (i >= 2 ? dp[i-2] : 0) + 2;
             } else if(i - dp[i-1] > 0 && s.charAt(i - dp[i-1] - 1) == '('){ //当 (())时
                dp[i] = dp[i-1] + ((i-dp[i-1]) >= 2 ? dp[i - dp[i-1] - 2] : 0) + 2;
             }
             res = Math.max(res, dp[i]);
        }
    }
    return res;
}

最长的覆盖子串

题目描述

给出两个字符串S和T,要求在O(n)的时间复杂度内在S中找出最短的包含T中所有字符的子串。
输入
“XDOYEZODEYXNZ”, “XYZ”
输出
“YXNZ”

方法

滑动窗口

代码实现

public String minWindow (String S, String T) {

       HashMap<Character, Integer> need, window;
       need = new HashMap<Character, Integer>();
       window = new HashMap<Character, Integer>();
 
       for(int i=0; i < T.length(); i++){
           char c = T.charAt(i);
           need.put(c,need.getOrDefault(c,0)+1);
       }
 
       int left = 0, right = 0;
       //包含目标T的字符数
       int valid = 0;
 
       //记录最小覆盖子串的起始索引及长度
       int start = 0, len = Integer.MAX_VALUE;
 
 
       //窗口不断往右移
       while(right < S.length()){
           char c = S.charAt(right);
 
           //右移窗口
           right++;
           //进行窗口内数据更新
           if(need.containsKey(c)){
               window.put(c,window.getOrDefault(c,0)+1);
               if(window.get(c) == need.get(c))
                   valid++;
           }
 
           //判断左侧是否进行收缩
           while(valid == need.size()){
               //更新最小覆盖子串
               if(right - left < len){
                   start = left;
                   len = right - left;
               }
 
               char d = S.charAt(left);
               //左移窗口
               left++;
 
               //进行窗口内数据更新
               if(need.containsKey(d)) {
                   if(window.get(d) == need.get(d))
                       valid--;
                   window.put(d,window.get(d)-1);
               }
           }
       }
       return len == Integer.MAX_VALUE ? "" : S.substring(start,start+len);
 
   }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值