字符串相关
1.KMP算法,判断子串是否在主串中存在,KMP的next数组还可以求字符串的最长重复子串,就是next数组中最大的值就是最长重复子串的长度。(我们需要从第1位每次往后都要重新求一次next数组)
abcabcabcabc 最大重复子串就是abcabcabc
然后这里我们对于next数组的最后一位做了一些处理
这种方法时间复杂度O(n^2)
public static int[] getNext(String t) { int pre=-1,now=0; int[] next=new int[t.length()+1]; next[0]=-1; while(now<t.length()-1) { if(pre==-1||t.charAt(pre)==t.charAt(now)) { pre++; now++; next[now]=pre; } else pre=next[pre]; } while(pre!=-1) { if(t.charAt(now)==t.charAt(pre)) { next[now+1]=pre+1; break; } else { pre=next[pre]; } } return next; } public static String getMaxRe(String t) { int len=0; int firstindex=0; for(int i=0;i<t.length();i++) { String temp=t.substring(i,t.length()); int[] next=getNext(temp); for(int j=1;j<next.length;j++) { if(next[j]>len) { len=next[j]; firstindex=i; } } } return t.substring(firstindex,firstindex+len); }
用后缀数组的思想来解决
时间复杂度:O(nlgn*N)N是字符串的长度,主要的时间复杂度在字符串排序
public static String bySuffixArray(String str) { String[] suffix=new String[str.length()]; for(int i=0;i<suffix.length;i++) { suffix[i]=str.substring(i,str.length()); } Arrays.sort(suffix);//对后缀数组排序 int maxRes=0; int index=0;//第几个后缀数组 for(int i=0;i<suffix.length-1;i++) { int temp=comPare(suffix[i], suffix[i+1]); if(temp>maxRes) { maxRes=temp; index=i; } } return suffix[index].substring(0, maxRes); } public static int comPare(String a,String b) { int i=0,j=0; int res=0; while(i<a.length()&&j<b.length()) { if(a.charAt(i)==b.charAt(j)) { res++; i++; j++; } else return res; } return res; }
计算一个字符串里面重复次数最多的子串:(还是可以用后缀数据和kmp)
public static String bySuffixArray(String str) { String[] suffix=new String[str.length()]; for(int i=0;i<suffix.length-1;i++) { suffix[i]=str.substring(i,str.length()); } HashMap<String, Integer> map=new HashMap<>(); for(int i=0;i<suffix.length-1;i++) { for(int j=1;j<suffix[i].length();j++) { String temp=suffix[i].substring(0,j+1); if(map.containsKey(temp)) { map.put(temp, map.get(temp)+1); } else { map.put(temp, 1); } } } String res=""; int maxR=0; for(String i:map.keySet()) { if(map.get(i)>maxR) { maxR=map.get(i); res=i; } } return res; }
利用kmp算法计算,一个子串是否能由它的一个子串连续组成
这里和我们之前求next数组还有点不一样,就是我们之前的next数组是将要去比较的小标,可能不相同,而我们这里是要明确相等的下标
所以在最后一位的时候需要去判断
public boolean repeatedSubstringPattern(String t) { if(t.length()==1||t==null) return false; if(t.length()==2) return t.charAt(0)==t.charAt(1); int pre=-1,now=0; int n=t.length(); int[] next=new int[t.length()]; next[0]=-1; while(now<t.length()-1) { if(pre==-1||t.charAt(pre)==t.charAt(now)) { pre++; now++; next[now]=pre; } else pre=next[pre]; } int last=t.charAt(next[n-1])==t.charAt(n-1)?next[n-1]+1:1;//这里相当于是计算最后一个子串的长度 return n%(n-last)==0?true:false; }
2.动态将01删除,当时腾讯的题,用栈贼简单,当时居然想着用动态规划,无语啊
String str=scan.nextLine(); char[] b=str.toCharArray(); Stack<Character> stack=new Stack<>(); stack.push(b[0]); for(int i=1;i<b.length;i++) { if(stack.isEmpty()) { stack.push(b[i]); continue; } if(stack.peek()!=b[i]) stack.pop(); else stack.push(b[i]); } System.out.println(stack.size());
3.求一个字符串的最长不重复子串。
直接上代码:自行理解
private static int findLongestSubstringLength(String string) { if (string == null || string.equals("")) return 0; int maxLength = 0; int curLength = 0; int[] positions = new int[26]; for (int i = 0; i < positions.length; i++) { positions[i] = -1; //初始化为-1,负数表示没出现过 } for (int i = 0; i < string.length(); i++) { int curChar = string.charAt(i) - 'a'; int prePosition = positions[curChar]; //当前字符与它上次出现位置之间的距离 int distance = i - prePosition; //当前字符第一次出现,或者前一个非重复子字符串中没有包含当前字符,跨越了这个距离,就是出现相同的那个元素是在当前范围之外的,也是可以继续加的 if(prePosition < 0 || distance > curLength) { curLength++; } else { //更新最长非重复子字符串的长度,出现相同的元素在我们当前的中间了,需要更新一次最大的,然后顺便把当前距离移动到我们相同的元素那个地方重新开始 if (curLength > maxLength) { maxLength = curLength; } curLength = distance; } positions[curChar] = i; //更新字符出现的位置 } if (curLength > maxLength) { maxLength = curLength; } return maxLength;
}
4.求两个字符串的最长公共子串,动态规划
dp[i][j]保存 以str1第i位字符结尾和str2第j位字符结尾的公共子串的长度,要求最长子串由最后一位结尾
public static int getLCS(String str1, String str2){ if(str1.length()==0||str2.length()==0) return 0; int n=str1.length(); int m=str2.length(); int[][] dp=new int[n+1][m+1]; int maxLen=0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { if(str1.charAt(i-1)==str2.charAt(j-1)) { dp[i][j]=dp[i-1][j-1]+1; } if(dp[i][j]>maxLen) { maxLen=dp[i][j]; } } return maxLen; }
求两个字符串的最长公共子序列,动态规划
dp[i][j]保存 以str1前i位字符串结尾和str2前j位字符串的公共子串的长度,不要求最长子序列以最后一位结尾
public static int getLCSubsequence(String str1, String str2){ if(str1.length()==0||str2.length()==0) return 0; int n=str1.length(); int m=str2.length(); int[][] dp=new int[n+1][m+1]; int maxLen=0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { if(str1.charAt(i-1)==str2.charAt(j-1)) dp[i][j]=dp[i-1][j-1]+1; else { dp[i][j]=Math.max(dp[i-1][j], dp[i][j-1]); } if(dp[i][j]>maxLen) { maxLen=dp[i][j]; } } return maxLen; }
5.实现一个Trie前缀树
public class Trie { public static class TrieNode { boolean isEnd = false; char val; //每一个节点可能有26个子节点 TrieNode[] child = new TrieNode[26]; TrieNode(char x) { val = x; } boolean hasNode(char node) { return child[node - 'a'] != null; } TrieNode findNode(char node) { return child[node - 'a']; } TrieNode createNode(char node) { child[node - 'a'] = new TrieNode(node); return child[node - 'a']; } } public Trie() {} TrieNode head = new TrieNode('-');//根节点不包含任何节点 public void insert(String word) { TrieNode index = head; for (char c : word.toCharArray()) { if (index.hasNode(c)) index = index.findNode(c); else index = index.createNode(c); } index.isEnd = true; } public boolean search(String word) { TrieNode index = head; for (char c : word.toCharArray()) if (index.hasNode(c)) index = index.findNode(c); else return false; return index.isEnd; } public boolean startsWith(String prefix) { TrieNode index = head; for (char c : prefix.toCharArray()) if (index.hasNode(c)) index = index.findNode(c); else return false; return true; } }
下面是leetcode里面一个对前缀数组的应用,先建前缀数组,再去做深度优先搜索
https://leetcode-cn.com/problems/word-search-ii/submissions/
给定一个二维网格 board 和一个字典中的单词列表 words,找出所有同时在二维网格和字典中出现的单词。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。
public class Trie { public static class TrieNode { boolean isEnd = false; char val; int count;//以这个节点结尾的单词个数 String str=null;//以这个节点结尾的字符串 //每一个节点可能有26个子节点 TrieNode[] child = new TrieNode[26]; TrieNode(char x) { val = x; } boolean hasNode(char node) { return child[node - 'a'] != null; } TrieNode findNode(char node) { return child[node - 'a']; } TrieNode createNode(char node) { child[node - 'a'] = new TrieNode(node); return child[node - 'a']; } } public Trie() {} TrieNode head = new TrieNode('-');//根节点不包含任何节点 public void insert(String word) { TrieNode index = head; for (char c : word.toCharArray()) { if (index.hasNode(c)) { index = index.findNode(c); } else index = index.createNode(c); } index.count++; index.str=word; index.isEnd = true; } public static ArrayList<String> findWords(char[][] board, String[] words) { if(board == null || board.length == 0 || board[0].length == 0) return null; Trie trie= new Trie(); for(String w : words){ trie.insert(w); } Set<String> hsRes = new HashSet<>(); for(int row = 0; row < board.length; row++){ for(int col = 0; col < board[0].length; col++){ Set<String> hs = search(board, trie.head, row, col); if(hs!=null) hsRes.addAll(hs); } } return new ArrayList<String>(hsRes); } private static int[][] sides = {{0,-1},{-1,0},{0,1},{1,0}}; private static Set<String> search(char[][] board, TrieNode node, int row, int col){ if(row < 0 || row >= board.length || col < 0 || col >= board[0].length) return null; char ch = board[row][col]; //已经走过了 if(ch == '#') return null; TrieNode child = node.child[ch-'a']; if(child == null) return null; Set<String> hsRes = new HashSet<>(); //字典树里面有这个字符串 if(child.str != null) { hsRes.add(child.str); // child.str = null; } board[row][col] = '#'; for(int[] side : sides){ Set<String> hs = search(board, child, row+side[0], col+side[1]); if(hs != null) { hsRes.addAll(hs); } } //保留现场 board[row][col] = ch; child.count -= hsRes.size(); return hsRes.size() > 0 ? hsRes : null; } public static void main(String[] args) { Trie trie= new Trie(); String[] words = {"oath","pea","eat","rain"}; char board[][] = { {'o','a','a','n'}, {'e','t','a','e'}, {'i','h','k','r'}, {'i','f','l','v'} }; ArrayList<String> res = Trie.findWords(board, words); for(String i:res) { System.out.println(i); } } }
6.查找字符串的第一个唯一字符
一定要用LinkedHashMap,可以记录你的插入顺序,我们这里需要的是第一个唯一字符
public static int firstUniqChar(String s) { LinkedHashMap<Character, Integer> map = new LinkedHashMap<>(); char[] chars = s.toCharArray(); for (char ch : chars) { map.put(ch, map.getOrDefault(ch, 0) + 1); } for (int i = 0; i < chars.length; i++) { if (map.get(chars[i]) == 1) { return i; } } return -1; }
7.分割回文串:给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
https://leetcode-cn.com/problems/palindrome-partitioning/
回溯法
ArrayList<ArrayList<String>> res=new ArrayList<>(); public boolean isParlindrome(String s){ //判断是否为回文串 if(s==""||s.length()==0){ return false; } 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; } public void backTracing(ArrayList<String> list,String s, int i){ if(i==s.length()){ res.add(list); } for(int j=i+1;j<=s.length();++j){ if(isParlindrome(s.substring(i,j))){ list.add(s.substring(i,j)); backTracing(new ArrayList<String> (list),s,j); list.remove(list.size()-1); } } } public ArrayList<ArrayList<String>> partition(String s) { if(s==""||s.length()==0){ return res; } ArrayList<String> list=new ArrayList<>(); backTracing(list,s,0); return res; }
8.分割字符串
https://leetcode.com/problems/word-break/
能够分割成字符串数组:
public boolean wordBreak(String s, List<String> wordDict) { if(wordDict.isEmpty()||s==null||s.length()==0) return false; boolean[] dp=new boolean[s.length()+1]; dp[0]=true; for(int i=1;i<dp.length;i++) { for(int j=i-1;j>=0;j--) { if(dp[j]&&wordDict.contains(s.substring(j, i))); { dp[i]=true; break; } } } return dp[s.length()]; } }
复杂版本的:
https://leetcode.com/problems/word-break-ii/
9.字符串压缩
abbbabbbcabbbabbbc
2[2[abbb]c]
public String Compress(String str) { int n=str.length(); String[][] dp=new String[n][n]; for (int step = 1; step <= n; ++step) {// length for(int i=0;i+step-1<n;i++) {//start int j=i+step-1;//end; dp[i][j]=str.substring(i, j+1); for(int k=i;k<j;k++) { String left=dp[i][k],right=dp[k+1][j]; if(left.length()+right.length()<dp[i][j].length()) { dp[i][j]=left+right; } } String t=str.substring(i,j+1); String replace=""; int pos=(t+t).indexOf(t, 1);//abab+abab找到第二个abab的下标,这样是为了解决abab这种2[ab]这种情况的 //如果下标大于等于原先长度表示没有重复 if (pos >= t.length()) { replace = t; } else { //有重复 replace = t.length() / pos + "[" + dp[i][i + pos - 1] + "]"; } if (replace.length() < dp[i][j].length()) { dp[i][j] = replace; } } } return dp[0][n-1];
}
10.和上面的对应,字符串解压缩,用两个栈来实现
public String Decode(String s) { Stack<Integer> s_num=new Stack(); Stack<String> s_str=new Stack(); int cnt=0; String t=""; for(int i=0;i<s.length();i++) { if(s.charAt(i)>='0'&&s.charAt(i)<='9') { cnt=10*cnt+s.charAt(i)-'0'; } else if(s.charAt(i)=='[') { s_num.push(cnt); s_str.push(t); cnt=0; t=""; } else if(s.charAt(i)==']') { int k=s_num.peek(); s_num.pop(); String temp=s_str.peek(); s_str.pop(); for(int j=0;j<k;j++) { temp+=t; } t=temp; } else { t+=s.charAt(i); } } return s_str.isEmpty()?t:s_str.peek(); }
本文来自博客园,作者:LeeJuly,转载请注明原文链接:https://www.cnblogs.com/peterleee/p/10704004.html
 
                    
                     
                    
                 
                    
                
 
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号