字符串相关

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();        
    }

 

posted @ 2019-04-14 09:36  LeeJuly  阅读(179)  评论(0)    收藏  举报