Skip to content

Latest commit

 

History

History
275 lines (264 loc) · 9.6 KB

字符串处理.md

File metadata and controls

275 lines (264 loc) · 9.6 KB

前缀树TrieTree

527.单词拼写

题目描述:给定一个由n个不重复非空字符串组成的数组,你需要按照以下规则为每个单词生成最小的缩写。

1.初始缩写由起始字母+省略字母的数量+结尾字母组成。
2.若存在冲突,亦即多于一个单词有同样的缩写,则使用更长的前缀代替首字母,直到从单词到缩写的映射唯一。
3.若缩写并不比原单词更短,则保留原样。
解析:若是两个字符串的缩写相同,我们就通过前缀树来求解每一个字符串的最短前缀。
public String addrecvString(String str, int i){
        int n = str.length();
        if (n <= i + 3)return str;
        return str.substring(0, 1 + i) + (n - 2 - i) + str.charAt(n - 1); 
    }
public List<String> wordsAbbreviation(List<String> dict){
    //1.构建map将字符串分组,拥有相同缩写的字符串被分到一组。
    Map<String, List<IndexWord>> map = new HashMap<>();
    String[] res = new String[dict.size()];
    int index = 0;
    for (String str : dict){
        String tmp = addrecvString(str, 0);
        if (!map.containsKey(tmp)){
            map.put(tmp, new ArrayList<IndexWord>());
        }
        map.get(tmp).add(new IndexWord(str, index));
        index++;
    }
    //2.遍历每一个组
    for (List<IndexWord> group : map.values()){
        TrieNode trie = new TrieNode();
        //2.1遍历组中每一个字符串构建trieTree
        for (IndexWord iw : group){
            TrieNode cur = trie;
            for (char ms : iw.word.substring(1).toCharArray()){
                if (cur.children[ms - 'a'] == null){
                    cur.children[ms - 'a'] = new TrieNode();
                }
                cur.count++;
                cur = cur.children[ms - 'a'];
            }
        }
        //2.2遍历构建的trieTree,来确定最小前缀。
        for (IndexWord iw : group){
            TrieNode cur = trie;
            int i = 0;
            for (char ms : iw.word.substring(1).toCharArray()){
                //若是count==1,则说明该节点只被访问过一次,符合最小前缀。
                if (cur.count == 1)break;
                cur = cur.children[ms - 'a'];
                i++;
            }
            res[iw.index] = addrecvString(iw.word, i);
        }
    }        
    return Arrays.asList(res);
}

class TrieNode{
    //前缀树节点,count记录当前节点有多少次被访问到
    TrieNode[] children;
    int count;
    TrieNode(){
        children = new TrieNode[26];
        count = 0;
    }
}

class IndexWord{
    String word;
    int index;
    IndexWord(String str, int i){
        word = str;
        index = i;
    }
}

滑动窗口

3.无重复字符的最长子串

题目描述:给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
解析:通过map记录每一个字符的下标,每次遇到重复的字符就找到该字符第一次出现的位置,并将窗口的左边界更新为该字符第一次出现的位置
public int lengthOfLongestSubstring(String s){
    int res = 0;
    char[] ss = s.toCharArray();
    HashMap<Character, Integer> map = new HashMap<Character, Integer>();
    int flag = -1;
    for (int i = 0; i < ss.length; i++){
        if (map.containsKey(ss[i]) && map.get(ss[i]) > flag){
            flag = map.get(ss[i]);//更新窗口左边界
        }
        res = Math.max(res, i - flag);
        if (res > ss.length - flag)break;
        map.put(ss[i], i);
    }        
    return res;
}

回文

5.最长回文子串

题目描述:给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
解析:回文题目首先想到manacher。
public String longestPalindrome(String s) {//manacher
    if (s.length() == 0)return s;
    char[] ss = getArray(s.toCharArray());
    int[] num = new int[ss.length];
    int pR = -1;
    int index = -1;
    int max = Integer.MIN_VALUE;
    int left = 0;
    for (int i = 0; i < num.length; i++){
        num[i] = i >= pR? 1 :Math.min(num[2 * index - i], pR - i);
        while(i + num[i] < ss.length && i - num[i] > -1 && ss[i + num[i]] == ss[i - num[i]]){
            num[i]++;
        }
        if (i + num[i] > pR){
            pR = i + num[i];
            index = i;
        }
        if (max < num[i]){
            max = num[i];
            left = (i - num[i] + 1) / 2;
        }
    }
    return s.substring(left, left + max - 1);
}

public char[] getArray(char[] ss){
    char[] res = new char[ss.length * 2 + 1];
    int index = 0;
    for (int i = 0; i < res.length; i++){
        res[i] = i%2==0?'#':ss[index++];
    }
    return res;
}

字符串匹配

10. 正则表达式匹配

题目描述:给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。
解析:每次的遍历都会分为两种进行判断:
    1.当前字符的下一个是不是'*'
        1.1 两字符能匹配上,p字符匹配多次(s后移一位)或者是p字符匹配零次(p后移两位)
        1.2 两字符不能匹配上,则p字符匹配零次(p后移两位)。
    2.下一个不是'*'
        或是两字符能匹配就继续匹配,否则直接结束。
public boolean isMatch(String s, String p) {
        if (s == null || p == null)return false;
        return run(s, 0, p, 0);
}
public boolean run(String s, int sIndex, String p, int pIndex){
    if (sIndex == s.length() && pIndex == p.length())return true;
    if (pIndex == p.length())return false;
    if (pIndex + 1 < p.length() && p.charAt(pIndex + 1) == '*'){
        if (sIndex != s.length() && (s.charAt(sIndex) == p.charAt(pIndex) || p.charAt(pIndex) == '.')){
            return run(s, sIndex + 1, p, pIndex) || run(s, sIndex, p, pIndex + 2);
        }else{
            return run(s, sIndex, p, pIndex + 2);
        }
    }else{
        if (sIndex != s.length() && (s.charAt(sIndex) == p.charAt(pIndex) || p.charAt(pIndex) == '.')){
            return run(s, sIndex + 1, p, pIndex + 1);
        }else{
            return false;
        }
    }
}

*.表示数值的字符串

题目描述:请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
解析:见代码
public boolean isNumeric(char[] str) {
    boolean hasE = false, hasDot = false, hasOpe = false;
    for (int i  = 0; i < str.length; i++){
        if (str[i] == 'e' || str[i] == 'E'){
            if (i == str.length - 1)return false;//e不能是最后一个
            if (hasE)return false;//e不能出现两次
            hasE = true;
        }else if (str[i] == '+' || str[i] == '-'){
            if (i > 0 && !hasOpe && !(str[i - 1] == 'e' || str[i - 1] == 'E'))return false;//+-第一次出现但不是开头前面必须是e(i>0是为了避开第一次出现在开头的情况)
            if (hasOpe && !(str[i - 1] == 'e' || str[i - 1] == 'E'))return false;//第二次出现前面必须是e
            hasOpe = true;
        }else if (str[i] == '.'){
            if (hasDot || hasE)return false;//小数点只能出现一次,且不能在e后面
            hasDot = true;
        }else{
            if (str[i] < '0' || str[i] > '9')return false;//字符需要合法
        }
    }
    return true;
}

*.字符流中第一个不重复的字符

题目描述:请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
解析:维护一个数组,每当有字符插入的时候就在该数组对应位置+1来表示该字符来过几次;维护一个list,每当有新的字符加入就判断该字符之前有没有出现过,若是没有就将其加入到list底部。
private int[] map = new int[256];
private LinkedList<Character> arr = new LinkedList<Character>();
public void Insert(char ch)
{
    map[ch]++;
    if (map[ch] == 1)arr.addLast(ch);
}
//return the first appearence once char in current stringstream
public char FirstAppearingOnce()
{
    if(arr.isEmpty())return '#';
    char ch = arr.peek();
    while (map[ch] != 1){
        arr.pop();
        if(arr.isEmpty())return '#';
        ch = arr.peek();
    }
    return ch;
}

DFS

17 电话号码的字母组合

题目描述:给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
解析:深度遍历实现
private String[] ss ={"abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
private List<String> res = new ArrayList<String>();
public List<String> letterCombinations(String digits) {
    if (digits == null || digits.length() == 0)return res;
    run("", digits);
    return res;
}
public void run(String str, String rest){
    if (rest.length() == 0){
        res.add(str);
    }else{
        String arr = ss[rest.charAt(0) - '2'];
        for (int i = 0; i < arr.length(); i++){
            run(str + arr.substring(i, i+1), rest.substring(1));
        }
    }
}