LeetCode 21-30题

本博客记录的是 LeetCode 21 到 30 题的题解

之前很少使用的语法

# 21 Merge k Sorted Lists
# 由两种纯暴力做法和两种对应的优化算法
# 堆可以优化快速查找最小值,二分可以优化合并的深度
# 二分一定要要注意递归的终点,l == r, l > r都是出来的
# python 中的堆讲解 这个模块名为heapq(其中的q表示队列
# heappush(heap, val), val = heappop(heap)
from heapq import *
class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        # 构造堆
        heap = []
        for i, l in enumerate(lists):
            if l:
                heappush(heap, (l.val, i))
        
        # 合并列表
        dummy = cur = ListNode()
        while (heap):
            val, i = heappop(heap)
            cur.next = lists[i]
            lists[i] = lists[i].next
            cur = cur.next
            if lists[i]:
                heappush(heap, (lists[i].val, i))

        return dummy.next   

# 28 python 字符串Hash
h[i] = (h[i - 1] * BASE + ord(s[i])) % MOD

# 29 加分的二进制倍增算法
# 30 非常容易出Bug的地方,因为ps.size()会变化
for (int i = j; i < ps.size(); i ++ ) {
	ps.pop_back();
}

21. Merge Two Sorted Lists

直接就是使用 头结点 dummy,然后进行指针移动即可。

c++代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode *dummy = new ListNode(), *cur = dummy;
        
        while (list1 || list2) {
            if (list1 == nullptr) {
                cur->next = list2;
                list2 = nullptr;
            } else if (list2 == nullptr) {
                cur->next = list1;
                list1 = nullptr;
            } else {
                if (list1->val <= list2->val) {
                    cur->next = list1;
                    list1 = list1->next;
                    cur = cur->next;
                } else {
                    cur->next = list2;
                    list2 = list2->next;
                    cur = cur->next;
                }
            }
        }
        return dummy->next;
    }
};

python 代码

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
        dummy = ListNode()
        cur = dummy
        while list1 or list2:
            if not list1:   # list1 is None
                cur.next = list2
                list2 = None
            elif not list2:
                cur.next = list1
                list1 = None
            else:
                if list1.val <= list2.val:
                    cur.next = list1
                    list1 = list1.next
                    cur = cur.next
                else:
                    cur.next = list2
                    list2 = list2.next
                    cur = cur.next
        return dummy.next

22. Generate Parentheses

一个挺简单的 DFS

C++ 代码

class Solution {
public:
    vector<string> res;
    int n;
    char s[110];
    vector<string> generateParenthesis(int x) {
        n = x;
        dfs(0, 0, 0);
        return res;
    }

    void dfs(int ptr, int cnt1, int cnt2) {
        if (ptr == 2 * n) {
            s[ptr] = '\0';
            res.push_back(string("") + s);
            return;
        }
        if (cnt2 > cnt1)    return;
        if (cnt1 < n) {
            s[ptr] = '(';
            dfs(ptr + 1, cnt1 + 1, cnt2);
        }
        if (cnt2 < n) {
            s[ptr] = ')';
            dfs(ptr + 1, cnt1, cnt2 + 1);
        }
    }
};

python 代码

class Solution:
    n = 0
    res = []
    s = []
    
    def generateParenthesis(self, k: int) -> List[str]:
        self.s = ['' for i in range(k * 2)]
        self.n = k
        self.res.clear()
        self.dfs(0, 0, 0)
        return self.res
    
    def dfs(self, ptr, cnt1, cnt2):
        if ptr == self.n * 2:
            self.res.append(''.join(self.s))
            return
        if cnt1 < cnt2:
            return
        if cnt1 < self.n:
            self.s[ptr] = '('
            self.dfs(ptr + 1, cnt1 + 1, cnt2)
        if cnt2 < self.n:
            self.s[ptr] = ')'
            self.dfs(ptr + 1, cnt1, cnt2 + 1)

23. Merge k Sorted Lists

纯暴力做法

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        ListNode *dummy = new ListNode(), *cur = dummy;
        int cur_val = -1, cur_idx = -1;
        int n = lists.size();
        while (true) {
            cur_idx = cur_val = -1;
            for (int i = 0; i < n; i ++ ) {
                if (lists[i] == nullptr)    continue;
                else if (cur_idx == -1 || lists[i]->val < cur_val) {
                    cur_idx = i, cur_val = lists[i]->val;
                }
            }
            if (cur_idx == -1) {
                break;
            } else {
                cur->next = lists[cur_idx];
                cur = cur->next;
                lists[cur_idx] = lists[cur_idx]->next;
            }
        }
        return dummy->next;
    }
};

使用堆优化

暴力做法中,我们不断循环,从 N 个链表中找到数最小的那个链表,进行插入,但是想要找到数字最小的链表,是 \(O(N)\)的,也就是说,太慢了。
最小值的话,不难想到直接使用堆进行维护!
c++ 代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
 typedef pair<int, int> PII;
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        // 初始化堆
        priority_queue<PII, vector<PII>, greater<PII> > que;
        int n = lists.size();
        for (int i = 0; i < n; i ++ ) {
            if (lists[i] != nullptr) {
                que.push(PII(lists[i]->val, i));
            }
        }

        // 插入数据
        ListNode *dummy = new ListNode(), *cur = dummy;
        PII tmp;    int idx;
        while (que.empty() == false) {
            idx = que.top().second;    que.pop();
            cur->next = lists[idx];
            cur = cur->next;
            lists[idx] = lists[idx]->next;
            if (lists[idx] != nullptr) {
                que.push(PII(lists[idx]->val, idx));
            }
        }
        
        return dummy->next;
    }
};

python 代码

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
from heapq import *
class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        # 构造堆
        heap = []
        for i, l in enumerate(lists):
            if l:
                heappush(heap, (l.val, i))
        
        # 合并列表
        dummy = cur = ListNode()
        while (heap):
            val, i = heappop(heap)
            cur.next = lists[i]
            lists[i] = lists[i].next
            cur = cur.next
            if lists[i]:
                heappush(heap, (lists[i].val, i))

        return dummy.next
            

使用二分进行合并

倘若,我们假设有 k 个有序列表\(list_1 ... list_k\),其中包含的元素个数分别是 \(n_1 .. n_k\),而且\(n=n_1+...+n_k\)
逐个进行两两合并的暴力做法的复杂度是:
\(n_1 * (k-1) + n_2 * (k-1) + n_3 * (k-2) + n_4 * (k - 3) + ... + n_{k-1} * 2 + n_k\)

那么,我们现在使用二分合并的思想(有点像归并排序)进行求解
一定要记得归并的递归终点
len == 1
len == 0

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    lists = []
    def mergeKLists(self, mylists: List[ListNode]) -> ListNode:
        # 一定要记得特判这个
        if not mylists: 
            return
        self.lists = mylists
        return self.myMergeKLists(0, len(self.lists) - 1)

    def myMergeKLists(self, l, r):
        if l == r:
            return self.lists[l]
        elif l + 1 == r:
            return self.mergeLists(self.lists[l], self.lists[r])
        else:
            return self.mergeLists(self.myMergeKLists(l, (l + r) // 2), self.myMergeKLists((l + r) // 2 + 1, r))

    def mergeLists(self, list1, list2):
        dummy = ListNode()
        cur = dummy
        while list1 and list2:
            if list1.val <= list2.val:
                cur.next = list1
                cur = cur.next
                list1 = list1.next
            else:
                cur.next = list2
                list2 = list2.next
                cur = cur.next
        if list1:
            cur.next = list1
        elif list2:
            cur.next = list2
        return dummy.next
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        # 一定要记得特判这个
        if not lists: 
            return
        if len(lists) == 1:
            return lists[0]
        mid = len(lists) // 2
        return self.mergeLists(self.mergeKLists(lists[:mid]), self.mergeKLists(lists[mid:]))

    def mergeLists(self, list1, list2):
        dummy = ListNode()
        cur = dummy
        while list1 and list2:
            if list1.val <= list2.val:
                cur.next = list1
                cur = cur.next
                list1 = list1.next
            else:
                cur.next = list2
                list2 = list2.next
                cur = cur.next
        if list1:
            cur.next = list1
        elif list2:
            cur.next = list2
        return dummy.next

24. Swap Nodes in Pairs

挺简单的一个指针交换

c++ 代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* swapPairs(ListNode* head) { 
        ListNode *dummy = new ListNode(), *cur = dummy, *cur1, *cur2;
        dummy->next = head;
        while (cur) {
            if (cur->next == nullptr) {
                break;
            }
            cur1 = cur->next;
            if (cur1->next == nullptr) {
                break;
            }
            cur2 = cur1->next;
            cur->next = cur2;
            cur1->next = cur2->next;
            cur2->next = cur1;
            cur = cur1;
        }
        return dummy->next;
    }
};

python代码

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def swapPairs(self, head: ListNode) -> ListNode:
        cur = dummy = ListNode()
        dummy.next = head
        while cur:
            cur1 = cur.next
            if not cur1:
                break
            cur2 = cur1.next
            if not cur2:
                break
            cur.next = cur2
            cur1.next = cur2.next
            cur2.next = cur1
            cur = cur1
        return dummy.next

25. Reverse Nodes in k-Group

使用数组交换指针

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
        dummy = cur = ListNode()
        dummy.next = head
        tmp = [None for i in range(k)]
        flag = True
        while cur and cur.next and flag:
            tmp[0] = cur.next
            for i in range(1, k):
                tmp[i] = tmp[i - 1].next
                if tmp[i] is None:
                    flag = False
                    break
            if flag == False:
                break
            cur.next = tmp[-1]
            tmp[0].next = tmp[-1].next
            for i in range(k - 1, 0, -1):
                tmp[i].next = tmp[i - 1]
            cur = tmp[0]
        return dummy.next

再来一种

# Definition for singly-linked list.
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next
class Solution:
    def reverseKGroup(self, head, k: int):
        if k == 1:
            return head
        dummy = cur = ListNode()
        dummy.next = head
        length = 0
        while cur.next:
            length += 1
            cur = cur.next
        print(length)
        cur = dummy
        
        for i in range(length // k):
            first_node = cur.next
            for j in range(k - 1):
                if j == 0:
                    p, q = cur.next, cur.next.next
                else:
                    p, q = q, tmp
                tmp = q.next
                q.next = p
            cur.next = q
            first_node.next = tmp
            cur = first_node
        
        return dummy.next

26. Remove Duplicates from Sorted Array

c++ coding

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if (nums.size() == 0) {
            return 0;
        }
        int j = 1, n = nums.size();
        for (int i = 1; i < n; i ++ ) {
            if (nums[i] != nums[i - 1]) {
                nums[j] = nums[i];
                j ++;
            }
        }
        return j;
    }
};

python coding

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        j, n = 1, len(nums)
        if n == 0:
            return 0
        for i in range(1, n):
            if nums[i] != nums[i - 1]:
                nums[j] = nums[i]
                j += 1
        return j

27. Remove Element

c++ coding

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int j = 0, n = nums.size();
        for (int i = 0; i < n; i ++ ) {
            if (val != nums[i]) {
                nums[j ++] = nums[i];
            }
        }
        return j;
    }
};

python coding

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        j, n = 0, len(nums)
        for i in range(n):
            if nums[i] != val:
                nums[j] = nums[i]
                j += 1
        return j

28. Implement strStr()

字符串Hash

c++ 代码

/*
个人感觉,要么是字符串hash,要么是 KMP算法解决
*/
typedef unsigned long long ULL;
const int N =  5 * 1e4 + 10, BASE = 31;
ULL a[N], p[N];
class Solution {
public:
    ULL get(int l, int r) {
        return a[r] - a[l - 1] * p[r - l + 1];
    }
    int strStr(string haystack, string needle) {
        int m = haystack.size(), n = needle.size();

        if (n == 0)   return 0;
        if (m < n)    return -1;

        // 预处理
        ULL target = 0;
        for (int i = 0; i < n; i ++ ) {
            target = target * BASE + needle[i] - 'a';
        }
        haystack = " " + haystack;
        m += 1;
        a[0] = 0;   p[0] = 1;
        for (int i = 1; i < m; i ++ ) {
            a[i] = a[i - 1] * BASE + haystack[i] - 'a';
            p[i] = p[i - 1] * BASE;
        }

        // Hash 判断    m - n --> m - 1  len: n
        for (int i = 1; i <= m - n; i ++ ) {
            if (get(i, i + n - 1) == target) {
                return i - 1;
            }
        }
        return -1;
    }
};

python 代码

"""
Hash ord
"""
class Solution:
    def modify(self, x, MOD):
        x %= MOD
        return x if x >= 0 else x + MOD

    def get(self, l, r, h, p, MOD):
        return self.modify(h[r] - h[l - 1] * p[r - l + 1] % MOD, MOD)

    def strStr(self, s: str, s2: str) -> int:
        N, BASE, MOD = 50010, 127, 1000000007
        
        target = 0
        for ch in s2:
            target = (target * BASE + ord(ch)) % MOD

        # hash
        s = " " + s
        n = len(s)
        h = [0] * (n + 10)
        p = [0] * (n + 10)
        h[0] = 0
        p[0] = 1
        for i in range(1, n):
            h[i] = (h[i - 1] * BASE + ord(s[i])) % MOD
            p[i] = p[i - 1] * BASE % MOD

        # match
        m = len(s2)
        for i in range(1, n - m + 1):
            if self.get(i, i + m - 1, h, p, MOD) == target:
                return i - 1
        return -1

KMP 解法

c++ 代码

const int N = 5e4 + 10;
int ne[N];
class Solution {
public:
    int strStr(string haystack, string needle) {
        if (needle == "")   return 0;
        if (needle.size() > haystack.size())    return -1;

        // initial
        int m = haystack.size(), n = needle.size();
        haystack = " " + haystack, needle = " " + needle;

        // kmp
        ne[0] = ne[1] = 0;
        int j = 0;
        for (int i = 2; i <= n; i ++ ) {
            while (j != 0 && needle[j + 1] != needle[i])    j = ne[j];
            if (needle[i] == needle[j + 1]) {
                ne[i] = ++ j;
            } else {
                ne[i] = j;
            }
        }

        // match
        j = 0;
        for (int i = 1; i <= m; i ++ ) {
            while (j != 0 && needle[j + 1] != haystack[i])    j = ne[j];
            if (haystack[i] == needle[j + 1]) {
                j += 1;
                if (j == n) {
                    return i - n;
                }
            }
        }
        return -1;
    }
};

python 代码

class Solution:
    def strStr(self, s: str, p: str) -> int:
        if p == '':
            return 0
        elif len(s) < len(p):
            return -1
        
        # KMP
        ne = [0] * 50010
        s, p = ' ' + s, ' ' + p
        m, n = len(s), len(p)
        j = 0
        for i in range(2, n):
            while j != 0 and p[j + 1] != p[i]:
                j = ne[j]
            if p[j + 1] == p[i]:
                j += 1
            ne[i] = j

        # Match
        j = 0
        for i in range(1, m):
            while j != 0 and p[j + 1] != s[i]:
                j = ne[j]
            if p[j + 1] == s[i]:
                j += 1
                if j == n - 1:
                    return i + 1 - n
        return -1

29. Divide Two Integers

不得不说,这题目我根本没有读懂题目含义,然后看的题解才知道他要干什么。
就是让我们仅仅使用加减法,算出来除数,为了优化时间复杂度,这里就是用了倍增算法

c++ 代码

class Solution {
public:
    int divide(int dividend, int divisor) {
        typedef long long LL;
        bool is_minus = false;
        if (dividend >= 0 && divisor < 0 || dividend <= 0 && divisor > 0) {
            is_minus = true;
        }
        
        // 注意里面也应该加入 LL,否则容易溢出
        LL x = abs(LL(dividend)), y = abs(LL(divisor));
        LL res = 0LL;
        vector<LL> v;
        for (LL i = y; i <= x; i = i + i) {
            v.push_back(i);
        }

        for (int i = v.size() - 1; i >= 0; i -= 1) {
            if (x >= v[i]) {
                x -= v[i];
                res += (1LL << i);
            }
        }
        if (is_minus) {
            res = -res;
        }

        if (res > (1LL << 31) - 1) {
            return (1LL << 31) - 1;
        } else if (res < -(1LL << 31)) {
            return -(1LL << 31);
        } else {
            return res;
        }
    }
};

python 代码

class Solution:
    def divide(self, x: int, y: int) -> int:
        is_minus = True if x >= 0 and y < 0 or x < 0 and y > 0 else False
        x, y = abs(x), abs(y)
        res = 0
        t = y
        while x >= y:
            t, i = y, 1
            while x >= t:
                x -= t
                res += i
                t += t
                i += i
        if is_minus:
            res = -res
        
        if res < -(2 ** 31):
            return -(2 ** 31)
        elif res > 2 ** 31 - 1:
            return 2 ** 31 - 1
        else:
            return res

30. Substring with Concatenation of All Words

解题思路:
首先,对于每一个word,利用算法(可以KMP或者字符串Hash)查看他出现的位置
并存储在 vector pos(s.size())中
然后,然后我们使用滑动数组来动态查找匹配的内容

KMP + 滑动数组

因为字符串可以重复,我们还需要预处理出来重复的出字符串,进行字符串->idx 转化
C++ 代码

class Solution {
public:
    void kmp(string &s, string &p, vector<int> &ne, int idx, vector<int> &pos) {
        ne[0] = ne[1] = 0;
        int n = p.size(), j = 0, m = s.size();
        for (int i = 2; i < n; i ++ ) {
            while (j && p[j + 1] != p[i])  j = ne[j];
            if (p[j + 1] == p[i])   j ++;
            ne[i] = j;
        }
        j = 0;
        for (int i = 1; i < m; i ++ ) {
            while (j && p[j + 1] != s[i])   j = ne[j];
            if (p[j + 1] == s[i]) j += 1;
            if (j == n - 1) {   // successful match 
                pos[i - n + 2] = idx;
                j = ne[j];
            }
        }
    }

    vector<int> findSubstring(string s, vector<string>& ps) {
        // 处理特殊情况 special judge
        vector<int> res;
        if (s.size() < ps.size() * ps[0].size()) {
            return res;
        }

        // 我们预处理ps,去除重复的元素,并统计他的目标数量
        int all_cnt = ps.size();
        unordered_map<string, int> tmp_m;
        int j = 0;
        vector<int> target(ps.size() + 10);
        for (int i = 0; i < ps.size(); i ++ ) {
            if (tmp_m.count(ps[i]) == 0) {
                tmp_m[ps[i]] = j;
                ps[j] = ps[i];
                target[j] = 1;
                j ++;
            } else {
                target[tmp_m[ps[i]]] += 1;
            }
        }
        int sz_tmp = ps.size();
        for (int i = j; i < sz_tmp; i ++ ) {
            ps.pop_back();
        }

        // initial and kmp
        vector<int> pos(s.size() + 10);
        vector<int> ne(s.size() + 10);
        for (int i = 0; i < pos.size(); i ++ ) {
            pos[i] = -1;
        }
        s = ' ' + s;
        for (int i = 0; i < ps.size(); i ++ ){
            ps[i] = ' ' + ps[i];
            kmp(s, ps[i], ne, i, pos);
        }

        
        // 滑动数组处理结果
        int m = s.size() - 1, n = ps[0].size() - 1, k = ps.size();
        vector<int> cnt(k + 10);    // 滑动数组
        for (int i = 1; i <= n; i ++ ) {    // 需要按照类别滑动n次,相当于模 n 剩余类
            // printf("i=%d->n=%d\n", i, n);
            for (int j = 0; j < k; j ++ ) 
                cnt[j] = 0;
            int pre = -1, cur_cnt = 0;;
            for (int j = i; j <= m; j += n) {   // 以 n 为步长,进行滑动窗口
                // printf("j=%d, pos[%d]=%d, pre=%d\n", j, j, pos[j], pre);
                if (pos[j] == -1) {
                    // cnt 清空
                    if (pre != -1) {
                        for (int u = pre; u < j; u += n) {
                            cnt[pos[u]] = 0;
                        }
                    }
                    pre = -1, cur_cnt = 0;
                } else {
                    if (cnt[pos[j]] == target[pos[j]]) {    // 以及达到预期目标
                        for (int u = pre; u < j; u += n) {
                            if (pos[u] == pos[j]) {
                                cnt[pos[u]] -= 1;
                                pre = u + n;
                                cur_cnt -= 1;
                                break;
                            } else {
                                cur_cnt -= 1;
                                cnt[pos[u]] -= 1;
                            }
                        }
                    }
                    if (pre == -1) {
                        pre = j;
                    }
                    cnt[pos[j]] += 1;
                    cur_cnt += 1;
                    if (cur_cnt == all_cnt) {
                        res.push_back(pre - 1);
                    }
                }
            }
        }
        
        return res;
    }
};

python 代码

class Solution:
    s = ''
    words = []
    pos = []
    ne = []

    def kmp(self, t):
        self.ne[0] = self.ne[1] = 0
        n, m = len(self.words[t]) - 1, len(self.s) - 1
        j = 0
        for i in range(2, n + 1):
            while j != 0 and self.words[t][j + 1] != self.words[t][i]:
                j = self.ne[j]
            if self.words[t][j + 1] == self.words[t][i]:
                j += 1
            self.ne[i] = j
        j = 0
        for i in range(1, m + 1):
            while j != 0 and self.words[t][j + 1] != self.s[i]:
                j = self.ne[j]
            if self.words[t][j + 1] == self.s[i]:
                j += 1
            if j == n:
                j = self.ne[j]
                self.pos[i - n + 1] = t
        
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        all_cnt = len(words)
        n, m = len(words[0]), len(s)
        # special judge
        if m < n * len(words):
            return []

        # words initialize
        words.sort()
        i, j, t = 1, 1, len(words)
        cnt = [1 for i in range(t)]
        for i in range(1, t):
            if words[i] != words[i-1]:
                words[j] = words[i]
                j += 1
            else:
                cnt[j - 1] += 1
        del words[j:t]
        del cnt[j:t]

        # kmp
        s = ' ' + s
        for i in range(len(words)):
            words[i] = ' ' + words[i]
        t = len(words)
        self.s, self.words = s, words
        self.pos = [-1 for i in range(m + 10)]
        self.ne = [-1 for i in range(m + 10)]
        for i in range(t):
            self.kmp(i)
        
        print(s)
        print(cnt)
        print(words)
        print(self.pos)

        # Sliding interval
        res = []
        cur_cnt = [0 for i in range(m // n + 10)]
        for i in range(1, n + 1):
            j, pre = i, -1
            print(f'i = {i}')
            for k in range(len(cur_cnt)):
                cur_cnt[k] = 0
            for j in range(i, m + 1, n):
                if self.pos[j] == -1:
                    if pre != -1:
                        k = pre
                        while k < j:
                            cur_cnt[self.pos[k]] = 0
                            k += n
                        pre = -1
                else:
                    # 回退
                    if pre != -1 and cur_cnt[self.pos[j]] >= cnt[self.pos[j]]:
                        while self.pos[pre] != self.pos[j]:
                            cur_cnt[self.pos[pre]] -= 1
                            pre += n
                        pre += n
                    else:
                        if pre == -1:
                            pre = j
                            cur_cnt[self.pos[pre]] += 1
                        else:
                            cur_cnt[self.pos[j]] += 1
                if pre != -1 and all_cnt == 1 + (j - pre) // n:
                    res.append(pre - 1)

        return res

Hash + 滑动数组

代码和 KMP 的差不多,仅仅是将 KMP部分换为了 HASH

class Solution:
    s = ''
    words = []
    pos = []
    ne = []

    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        BASE, MOD, A_NUM = 127, int(1e9+7), ord('a')
        all_cnt = len(words)
        n, m = len(words[0]), len(s)
        # special judge
        if m < n * len(words):
            return []

        # words initialize
        words.sort()
        i, j, t = 1, 1, len(words)
        cnt = [1 for i in range(t)]
        for i in range(1, t):
            if words[i] != words[i-1]:
                words[j] = words[i]
                j += 1
            else:
                cnt[j - 1] += 1
        del words[j:t]
        del cnt[j:t]

        # hash
        s = ' ' + s
        for i in range(len(words)):
            words[i] = ' ' + words[i]
        t = len(words)
        self.s, self.words = s, words
        self.pos = [-1 for i in range(m + 10)]
        str_to_number = {}
        my_max_p = 1
        for i in range(1, n):   # n - 1 times
            my_max_p = my_max_p * BASE % MOD    
        for i, p in enumerate(words):
            cur_ord = 0
            for ch in p[1:]:
                cur_ord = (cur_ord * BASE + ord(ch) - A_NUM) % MOD
            str_to_number[cur_ord] = i
        cur_ord = 0
        for i in range(1, m + 1):
            cur_ord = (cur_ord * BASE + ord(s[i]) - A_NUM) % MOD
            if i < n:
                continue
            elif i >= n:
                if cur_ord in str_to_number.keys():
                    self.pos[i - n + 1] = str_to_number[cur_ord]
                else:
                    self.pos[i - n + 1] = -1
                cur_ord = cur_ord - my_max_p * (ord(s[i-n+1]) - A_NUM)
                cur_ord %= MOD
                cur_ord = cur_ord + MOD if cur_ord < 0 else cur_ord
        
        
        print(s)
        print(cnt)
        print(words)
        print(self.pos)

        # Sliding interval
        res = []
        cur_cnt = [0 for i in range(m // n + 10)]
        for i in range(1, n + 1):
            j, pre = i, -1
            print(f'i = {i}')
            for k in range(len(cur_cnt)):
                cur_cnt[k] = 0
            for j in range(i, m + 1, n):
                if self.pos[j] == -1:
                    if pre != -1:
                        k = pre
                        while k < j:
                            cur_cnt[self.pos[k]] = 0
                            k += n
                        pre = -1
                else:
                    # 回退
                    if pre != -1 and cur_cnt[self.pos[j]] >= cnt[self.pos[j]]:
                        while self.pos[pre] != self.pos[j]:
                            cur_cnt[self.pos[pre]] -= 1
                            pre += n
                        pre += n
                    else:
                        if pre == -1:
                            pre = j
                            cur_cnt[self.pos[pre]] += 1
                        else:
                            cur_cnt[self.pos[j]] += 1
                if pre != -1 and all_cnt == 1 + (j - pre) // n:
                    res.append(pre - 1)

        return res
posted @ 2021-12-27 22:12  lucky_light  阅读(61)  评论(0编辑  收藏  举报