[leetcode 30 串聯所有單詞的子串 10ms]

fishcanfly發表於2024-06-08

演算法複雜度 o(1):

  • 複雜最壞複雜度 是 o(s.length) 和 o(m*total)的最大值
    碼程式碼速度要變快,變數,演算法要先想清楚

import java.util.*;

class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        m = words[0].length();
        n = words.length;

        cnt = new HashMap<>();

        int total = 0;
        for (String word : words) {
            cnt.put(word, cnt.getOrDefault(word, 0) + 1);
            total += word.length();
        }

        List<Integer> ans = new LinkedList<>();
        // map[j]<str> 表示從[i,i+total -1]足要求的string的要求的出現各個位置
        map = new HashMap[m];
        int[] dp = new int[s.length()];
        for (int i = 0; i < dp.length;i++) {
            dp[i] = i + 1;
        }
        for (int i = 0; i < m; i++) {
            if (i + total > s.length()) {
                break;
            }
            boolean find = true;
            map[i] = new HashMap<String, LinkedList<Integer>>();
            for (int j = i + total - 1; j >= i + m - 1; j = j - m) {
                String str = s.substring(j - m + 1, j + 1);
                int num = cnt.getOrDefault(str, 0);
                int occur = map[i].getOrDefault(str, new LinkedList<>()).size();
                if (num > occur) {
                    // exist, 並且出現次數少
                    map[i].putIfAbsent(str, new LinkedList<>());
                    // 記錄開始位置
                    map[i].get(str).addFirst(j - m + 1);
                    dp[i + total - 1] = j - m + 1;
                } else {
                    find = false;
                    break;
                }
            }
            if (find) {
                ans.add(i);
            }
        }
        // 遍歷後面的
        for (int j = m; j <= s.length() - total; j++) {
            int tail = j + total - 1;

            String str = s.substring(j - m + total, j + total);

            if (cnt.getOrDefault(str, 0) == 0) {
                // 當前字串不被包含;
                dp[tail] = tail + 1;
                continue;
            }

            // 上一個最遠的位置
            int leftmost = dp[j + total - 1 - m];
            // 上個滿足要求
            while (!map[j % m].getOrDefault(str, new LinkedList<>()).isEmpty() && map[j % m].get(str).getFirst() < leftmost) {
                map[j % m].get(str).pollFirst();
            }

            if (leftmost == j - m) {
                // 當前位置的最遠, 預設能到達
                int newMostleft = j;
                if (!map[j % m].get(str).isEmpty()) {
                    // 肯定會到達這裡, 因為該str肯定會被前面包含
                    newMostleft = map[j % m].get(str).pollFirst();
                }
                map[j % m].get(str).add(tail - m + 1);
                if (newMostleft == j - m) {
                    // 相同
                    dp[tail] = j;
                    ans.add(j);
                } else {
                    // 不相同
                    dp[tail] = newMostleft + m;
                }
            } else {
                int num = map[j % m].getOrDefault(str, new LinkedList<>()).size();
                int all = cnt.get(str);

                map[j % m].putIfAbsent(str, new LinkedList<>());
                map[j % m].get(str).add(tail -m + 1);
                if (num == all) {
                    int entry = map[j % m].get(str).getFirst();
                    // 順序
                    dp[tail] = entry + m;
                    map[j % m].get(str).pollFirst();
                }
                else {
                    // 更新為最
                    dp[tail] = leftmost;
                    if (tail - leftmost + 1  ==  total) {
                        ans.add(j);
                    }
                }
            }
        }
        return ans;
    }

    int n;
    int m;
    int k = 0;
    Map<String, Integer> cnt;
    Map<String, LinkedList<Integer>>[] map;

    public static void main(String[] args) {
        TreeSet<Integer> set;
        Solution solution = new Solution();
        List<Integer> ans = solution.findSubstring("aaa", new String[]{
                "a", "a"
        });
        System.out.println(ans);
    }
}

演算法2, trie樹匹配

import java.util.ArrayList;
import java.util.List;

class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        build(words);
        int n = words.length;
        this.words = words;
        int cnt = 0;
        for (String word : words) {
            cnt += word.length();
        }

        List<Integer> ans = new ArrayList<>();
        for (int i = 0; i <= s.length() - cnt; i++) {
            find = false;
            dfs(s, i, 0);
            if (find) {
                ans.add(i);
            }
        }
        return ans;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        List<Integer> ans = solution.findSubstring("wordgoodgoodgoodbestword", new String[]{
                "word","good","best","good"
        });
        System.out.println(ans);
    }

    boolean find = false;
    String[] words;

    public void dfs(String s, int start, int depth) {
        if (find) {
            return;
        }
        if (depth == words.length) {
            find = true;
            return;
        }

        if (start == s.length()) {
            return;
        }

        char ch = s.charAt(start);
        int idx = ch - 'a';

        if (root[idx] == null) {
            return;
        }

        TrieNode node = null;
        while (start < s.length()) {
            node = node == null ? root[idx] : node.findChild(s.charAt(start));
            start++;
            if (node == null) {
                return;
            }
            while (node.indexList.isEmpty() && start < s.length()) {
                node = node.findChild(s.charAt(start++));
                if (node == null) {
                    return;
                }
            }
            if (node.indexList.isEmpty()) {
                return;
            }
            int index = node.indexList.get(node.indexList.size() - 1);
            node.indexList.remove(node.indexList.size() - 1);

            dfs(s, start , depth + 1);

            node.indexList.add(index);
            if (start == s.length()) {
                return;
            }
        }

    }


    class TrieNode {
        TrieNode[] children = new TrieNode[26];
        char ch;

        int idx;

        List<Integer> indexList;

        public TrieNode(char ch) {
            this.ch = ch;
            children = new TrieNode[26];
            this.idx = ch - 'a';
            indexList = new ArrayList<>();
        }

        public TrieNode addChild(char ch) {
            if (children[ch - 'a'] == null) {
                children[ch - 'a'] = new TrieNode(ch);
            }
            return children[ch - 'a'];
        }

        public TrieNode findChild(char ch) {
            return children[ch - 'a'];
        }

        public void terminal(int index) {
            indexList.add(index);
        }

        public boolean hasTerminal() {
            return indexList.isEmpty();
        }

    }

    TrieNode[] root = new TrieNode[26];

    public void build(String[] words) {
        for (int i = 0; i < words.length; i++) {
            buildTrie(words[i], i);
        }
    }

    public void buildTrie(String word, int index) {
        char ch = word.charAt(0);
        int idx = ch - 'a';
        if (root[idx] == null) {
            root[idx] = new TrieNode(ch);
        }
        TrieNode curr = root[idx];
        for (int i = 1; i < word.length(); i++) {
            curr = curr.addChild(word.charAt(i));
        }
        curr.indexList.add(index);
    }
}

相關文章