感觉对字符串的问题没有一个系统的总结,所以打算记录下比较经典的题目。
3. 无重复字符的最长子串
1.第一种方法我是用双重循环,加入以i为起点,用 j 遍历到出现了i,j之间相同的字符用让起点 i++。
public class Solution_1 {public static void main(String[] args) {System.out.println(lengthOfLongestSubstring("bbbbb"));}public static int lengthOfLongestSubstring(String s) {char[] ch = s.toCharArray();int max = 0;for(int i = 0; i < ch.length; i++) {String str = "";for(int j = i; j < ch.length; j++) {if(str.indexOf(ch[j]) == -1)str = str + ch[j]+"";elsebreak;}max = max > str.length() ? max : str.length();}return max;}}
- 用集合 set代替一重循环,因为set有cantains。 ```java import java.util.HashMap; import java.util.HashSet;
public class Solution_2 {
public static void main(String[] args) {
System.out.println(lengthOfLongestSubstring(“bbbbb”));
}
public static int lengthOfLongestSubstring(String s) {
int max = 0;
HashSet
int i = 0, j = 0;//滑动窗口while(i < s.length() && j < s.length()) {if(!set.contains(s.charAt(j))) {set.add(s.charAt(j++));max = Math.max(max, j - i);}elseset.remove(s.charAt(i++));}return max;}
}
3. 可以在起点上做一点优化,起点不需要依次递增,如果出现重复字符,只需把起点移动到 string(i,j)之间的那个重复字符的下一位即可。如图所示,当子串abcd 又碰到c时, 按以前方法,起点 i 还要依次从b和c开始,但是不需要这么做,因为只要c还在,那么字串 b c d c, c d c 一定会出现相同字符,而且长度一定小于 a b c d c,所以起点完全可以从 d 开始。```javaimport java.util.HashMap;import java.util.Map;public class Solution_3 {public int lengthOfLongestSubstring(String s) {int n = s.length(), ans = 0;Map<Character, Integer> map = new HashMap<>(); // current index of character//关键在于i的优化,在当前子串索引i到j。如果包括末端j字符,那么i直接跳到子串中相同元素位置m,因为只要有m存在,那么字串就无法向后移动// try to extend the range [i, j]for (int j = 0, i = 0; j < n; j++) {if (map.containsKey(s.charAt(j))) {i = Math.max(map.get(s.charAt(j)), i);}ans = Math.max(ans, j - i + 1);map.put(s.charAt(j), j + 1);}return ans;}}
5. 最长回文子串
1.我开始的思路是遍历每个字符,然后循环判断该两边的字符是否相同。这样问题就是只适合回文串是奇数的时候。所以当回文串是偶数我们需要从 i 和 i + 1两个相邻的位置同时往外延伸。然后取两者最大值。
public class Solution_3 {public static void main(String[] args) {System.out.println(longestPalindrome("abba"));}public static String longestPalindrome(String s) {if(s.equals(""))return "";int maxLength = 0, start = 0, end = 0;for(int i = 0; i < s.length(); i++) {int len1 = fun1(s, i, i); //以当前i位置字符为中心向两边延伸int len2 = fun1(s, i, i + 1); //以i位置和i+1位置的中心向两边延伸maxLength = Math.max(len1, len2);if(maxLength > end - start) {start = i - (maxLength - 1) / 2; //当回文串为奇数,中心节点就是i, 可以直接减去 maxLength / 2 ,// 但是回文串为偶数时, 中心节点时 i和i+1之间的点,此时索引=i和中心节点与左半回文串相差 1/2// 减去(maxLength - 1) / 2//当回文串为奇数 maxLength / 2 == (maxLength - 1) / 2,所以无需分类讨论end = maxLength / 2 + i; //奇数同理直接加上maxLength / 2 同理回文串为偶数时,中心节点时 i和i+1之间的点,此时索引=i和中心节点与右半回文串相差 1/2//但是maxLength / 2 == (maxLength + 1) / 2}}return s.substring(start, end + 1);}private static int fun1(String s, int left, int right) {while(left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {left--; right++;}return right - left - 1;}}
14. 最长公共前缀
暴力解法肯定可以的,这里主要采用了字典树,包括初始化树的结构, 这里不再是左右子树,而是26个,所以我们需要一个集合,而且还需要根据恰当的索引找到对应的字典树节点, 所以采用了HashMap。 方法主要包括增加字符串, 获取最长公共前缀等等。
import java.util.TreeMap;public class Solution_2 {public static void main(String[]args){System.out.println(longestCommonPrefix(new String[]{"flower","flow","flight"}));}public static String longestCommonPrefix(String[] strs) {Trie trie = new Trie();for(int i = 0; i < strs.length; i++) {trie.add(strs[i]);}return trie.searchLongestPrefix();}}class Trie {private class TrieNode {public int size; //当前节点有多少个子节点public TreeMap<Character, TrieNode> nodes; //当前节点的链接节点public boolean isEnd; //是否是一个单词public TrieNode(boolean isEnd) {this.isEnd = isEnd;nodes = new TreeMap<>();}public TrieNode() {this(false);}}TrieNode root;public Trie() {root = new TrieNode();}public void add(String string) {TrieNode cur = root;for(int i = 0; i < string.length(); i++) {char c = string.charAt(i);if(cur.nodes.get(c) == null)cur.nodes.put(c, new TrieNode());cur = cur.nodes.get(c);}cur.isEnd = true;}//找出字典树中的最长前缀public String searchLongestPrefix() {String prefix = "";TrieNode node = root;while(!node.isEnd && node.nodes.size() == 1) {char c = node.nodes.firstKey();prefix += c+"";node = node.nodes.get(c);}return prefix;}}
最长公共子串
题目描述:给定两个字符串,求出他们之间最长相同子字符串长度。
1、 状态定义:dp[i][j]表示以s1[i]和s2[j]为最后一个元素的最长公共子串,如果最长公共子串存在,公共子串的最后一个元素一定是s1[i]或者s2[j]他们相等。
2、 状态转移方程:
如果s1[i]和s2[j]相等,那么:dp[i][j] = dp[i-1][j-1]+1;
如果s1[i]和s2[j]不相等,则:dp[i][j] = 0;
3、 初始化:初始dp[0][j]和dp[i][0]都为false
4、 输出:dp[i][j]的最大值
public int longestCommonString(String s1,String s2) {int[][] dp = new int[s1.length()+1][s2.length()+1];int maxlen = 0;for (int i = 1; i <= s1.length(); i++) {for (int j = 1; j <= s2.length(); j++) {if(s1.charAt(i-1)==s2.charAt(j-1))dp[i][j] = dp[i-1][j-1]+1;elsedp[i][j] = 0;maxlen = Math.max(maxlen, dp[i][j]);}}return maxlen;}
