Leetcode:字符串


滑动窗口

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

public int lengthOfLongestSubstring(String s) {
       //记录子串最大长度
		int len = 0;
		//记录无重复字符及其所在位置
		Map<Character, Integer> map = new HashMap<>();
		//循环遍历字符串,i,j分别为左右边界
		for (int i = 0, j = 0; j < s.length(); j++) {
			//判断map中是否含有该字符
			if (map.containsKey(s.charAt(j))) {
				i = Math.max(map.get(s.charAt(j)), i);
			}
			//新子串长度与原子串长度取最大值
			len = Math.max(len, j-i+1);
			//将新字符存入map中
			map.put(s.charAt(j), j+1);
		}
		return len;
    }

来源
链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/solution/hua-dong-chuang-kou-by-powcai/

思路滑动窗口

什么是滑动窗口?

其实就是一个队列,比如例题中的 abcabcbb,进入这个队列(窗口)为 abc 满足题目要求,当再进入 a,队列变成了 abca,这时候不满足要求。所以,我们要移动这个队列!

如何移动?

我们只要把队列的左边的元素移出就行了,直到满足题目要求!

一直维持这样的队列,找出队列出现最长的长度时候,求出解!

该题中,巧妙的使用了hashmap的key重复时会覆盖value值的特性,使得遇到重复元素的时候,旧元素会被新元素替代,当下次新元素再次重复时,新元素value所代表的索引不会因为旧元素存在而起冲突。

len记录了最大子串的长度,在遍历String s 的过程中,旧子串不断与新子串的长度进行对比,获取最大子串长度,实现一次遍历找到最大子串。

图解:
https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/solution/hua-jie-suan-fa-3-wu-zhong-fu-zi-fu-de-zui-chang-z/

时间复杂度:O(n)

滑动窗口题目:
3. 无重复字符的最长子串
30. 串联所有单词的子串
76. 最小覆盖子串
159. 至多包含两个不同字符的最长子串
209. 长度最小的子数组
239. 滑动窗口最大值
567. 字符串的排列
632. 最小区间
727. 最小窗口子序列


动态规划、中心扩展、Manacher

1、给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

	public String getPalindrome(String s) {
		int len = s.length();
		if (len <= 1) {
			return s;
		}
		int longestPalindrome = 1;
		String longestPalindromeStr = s.substring(0, 1);
		boolean[][] dp = new boolean[len][len];
		// abcdedcba
		// l r
		// 如果 dp[l, r] = true 那么 dp[l + 1, r - 1] 也一定为 true
		// 关键在这里:[l + 1, r - 1] 一定至少有 2 个元素才有判断的必要
		// 因为如果 [l + 1, r - 1] 只有一个元素,不用判断,一定是回文串
		// 如果 [l + 1, r - 1] 表示的区间为空,不用判断,也一定是回文串
		// [l + 1, r - 1] 一定至少有 2 个元素 等价于 l + 1 < r - 1,即 r - l > 2

		// 写代码的时候这样写:如果 [l + 1, r - 1] 的元素小于等于 1 个,即 r - l <= 2 ,就不用做判断了

		// 因为只有 1 个字符的情况在最开始做了判断
		// 左边界一定要比右边界小,因此右边界从 1 开始
		for (int r = 1; r < len; r++) {
			for (int l = 0; l < r; l++) {
				// 区间应该慢慢放大
				// 状态转移方程:如果头尾字符相等并且中间也是回文
				// 在头尾字符相等的前提下,如果收缩以后不构成区间(最多只有 1 个元素),直接返回 True 即可
				// 否则要继续看收缩以后的区间的回文性
				// 重点理解 or 的短路性质在这里的作用
				if (s.charAt(l) == s.charAt(r) && (r - l <= 2 || dp[l + 1][r - 1])) {
					dp[l][r] = true;
					if (r - l + 1 > longestPalindrome) {
						longestPalindrome = r - l + 1;
						longestPalindromeStr = s.substring(l, r + 1);
					}
				}
			}
		}
		return longestPalindromeStr;
	}

动态规划用来解决最优子结构问题:

  • 定义 “状态”
  • 找到 “状态转移方程”。

时间复杂度:O(n2)
空间复杂度:O(n2)

中心扩展算法,Manacher算法:
https://leetcode-cn.com/problems/longest-palindromic-substring/solution/zhong-xin-kuo-san-dong-tai-gui-hua-by-liweiwei1419/



2、给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。

public List<String> generateParenthesis(int n) {
		//创建链表,每个元素代表有几个括号,链表中每个元素都是一个链表,用来存储每种情况
		LinkedList<LinkedList<String>> result = new LinkedList<LinkedList<String>>();
		if (n == 0)
			return result.get(0);
		LinkedList<String> list0 = new LinkedList<String>();
		list0.add("");
		result.add(list0);
		LinkedList<String> list1 = new LinkedList<String>();
		list1.add("()");
		result.add(list1);
		for (int i = 2; i <= n; i++) {
			LinkedList<String> temp = new LinkedList<String>();
			for (int j = 0; j < i; j++) {
				List<String> str1 = result.get(j);
				List<String> str2 = result.get(i - 1 - j);
				for (String s1 : str1) {
					for (String s2 : str2) {
						String el = "(" + s1 + ")" + s2;
						temp.add(el);
					}
				}

			}
			result.add(temp);
		}
		return result.get(n);
	}

思路

	来源:https://leetcode-cn.com/problems/generate-parentheses/solution/zui-jian-dan-yi-dong-de-dong-tai-gui-hua-bu-lun-da/

当我们清楚所有 i 它一定是一个左括号,那么它可以和它对应的右括号组成一组完整的括号 “( )”,我们认为这一组是相比 n-1 增加进来的括号。

那么,剩下 n-1 组括号有可能在哪呢?

【这里是重点,请着重理解】

剩下的括号要么在这一组新增的括号内部,要么在这一组新增括号的外部(右侧)。

既然知道了 i

“(” + 【i=p时所有括号的排列组合】 + “)” + 【i=q时所有括号的排列组合】

其中 p + q = n-1,且 p q 均为非负整数。

事实上,当上述 p 从 0 取到 n-1,q 从 n-1 取到 0 后,所有情况就遍历完了。

注:上述遍历是没有重复情况出现的,即当 (p1,q1)≠(p2,q2) 时,按上述方式取的括号组合一定不同。


回溯

题目:给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

Leetcode:字符串_第1张图片

	Map<String, String> phone = new HashMap<String, String>() {
		{
			put("2", "abc");
			put("3", "def");
			put("4", "ghi");
			put("5", "jkl");
			put("6", "mno");
			put("7", "pqrs");
			put("8", "tuv");
			put("9", "wxyz");
		}
	};

	List<String> output = new ArrayList<String>();

	public void backtrack(String combination, String next_digits) {
		// if there is no more digits to check
		if (next_digits.length() == 0) {
			// the combination is done
			output.add(combination);
		}
		// if there are still digits to check
		else {
			// iterate over all letters which map
			// the next available digit
			String digit = next_digits.substring(0, 1);
			String letters = phone.get(digit);
			for (int i = 0; i < letters.length(); i++) {
				String letter = phone.get(digit).substring(i, i + 1);
				// append the current letter to the combination
				// and proceed to the next digits
				backtrack(combination + letter, next_digits.substring(1));
			}
		}
	}

	public List<String> letterCombinations(String digits) {
		if (digits.length() != 0)
			backtrack("", digits);
		return output;
	}

时间复杂度: O(3^n * 4^m)。其中 N 是输入数字中对应 3 个字母的数目(比方说 2,3,4,5,6,8), M 是输入数字中对应 4 个字母的数目(比方说 7,9),N+M 是输入数字的总数。

空间复杂度: O(3^n * 4^m)


质数映射

题目:给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。

链接:https://leetcode-cn.com/problems/group-anagrams/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by–16/

思路

算术基本定理,又称为正整数的唯一分解定理,即:每个大于1的自然数,要么本身就是质数,要么可以写为2个以上的质数的积,而且这些质因子按大小排列之后,写法仅有一种方式。

利用这个,我们把每个字符串都映射到一个正数上。

用一个数组存储质数 prime = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103}。

然后每个字符串的字符减去 ’ a ’ ,然后取到 prime 中对应的质数。把它们累乘。

例如 abc ,就对应 ‘a’ - ‘a’, ‘b’ - ‘a’, ‘c’ - ‘a’,即 0, 1, 2,也就是对应素数 2 3 5,然后相乘 2 * 3 * 5 = 30,就把 “abc” 映射到了 30。

public List<List<String>> groupAnagrams(String[] strs) {
    HashMap<Integer, List<String>> hash = new HashMap<>();
    //每个字母对应一个质数
    int[] prime = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103 };
    for (int i = 0; i < strs.length; i++) {
        int key = 1;
        //累乘得到 key
        for (int j = 0; j < strs[i].length(); j++) {
            key *= prime[strs[i].charAt(j) - 'a'];
        } 
        if (hash.containsKey(key)) {
            hash.get(key).add(strs[i]);
        } else {
            List<String> temp = new ArrayList<>();
            temp.add(strs[i]);
            hash.put(key, temp);
        }

    }
    return new ArrayList<List<String>>(hash.values());
}

时间复杂度:O(n * K)

空间复杂度:O(NK)

你可能感兴趣的:(#,Basics,------,Algorithm)