单词游戏

Submission

运行时间: 96 ms

内存: 42.2 MB

class Solution:
    def canAliceWin(self, a: List[str], b: List[str]) -> bool:
        left = [''] * 27
        right = [''] * 27
        for s in a:
            left[ord(s[0])-ord('a')] = s
        
        for s in b:
            right[ord(s[0])-ord('a')] = s
        
        
        def pick(last,arr):
            i = ord(last[0])-ord('a')
            if i < 25:
                if arr[i+1]:
                    return arr[i+1]
                                       
            if arr[i] and arr[i] > last:
                return arr[i]
                
            return last
        
        # 当前能否获胜
        @cache
        def dfs(last,flag):
            i = ord(last[0])-ord('a')
            # a选
            if flag:
                if left[i] and left[i] > last:
                    if not dfs(left[i],not flag):
                        return True
                
                if left[i+1]:
                    if not dfs(left[i+1],not flag):
                        return True
                return False
            # b选
            else:
                if right[i] and right[i] > last:
                    if not dfs(right[i],not flag):
                        return True
                
                if right[i+1]:
                    if not dfs(right[i+1],not flag):
                        return True
                return False
                    
        return not dfs(a[0],False)
                                                    

Explain

这个题解的基本思路是使用深度优先搜索(DFS)来模拟两个玩家(Alice 和 Bob)轮流从两个不同的单词列表中选择单词的游戏。玩家必须选择的单词需要字母顺序大于上一个选择的单词,并且以相邻的字母开头。解题关键是利用缓存(通过@cache装饰器)来避免重复计算相同状态下的结果,提高效率。通过递归调用`dfs`函数来判断当前玩家是否有必胜策略。如果当前玩家能选择一个单词,使得对方在下一轮没有必胜策略,则当前玩家获胜。

时间复杂度: O(1)

空间复杂度: O(1)

class Solution:
    def canAliceWin(self, a: List[str], b: List[str]) -> bool:
        left = [''] * 27
        right = [''] * 27
        for s in a:
            left[ord(s[0])-ord('a')] = s
        
        for s in b:
            right[ord(s[0])-ord('a')] = s
        
        @cache
        def dfs(last, flag):
            i = ord(last[0])-ord('a')
            if flag:  # Alice's turn
                if left[i] and left[i] > last:
                    if not dfs(left[i], not flag):
                        return True
                if left[i+1]:
                    if not dfs(left[i+1], not flag):
                        return True
                return False
            else:  # Bob's turn
                if right[i] and right[i] > last:
                    if not dfs(right[i], not flag):
                        return True
                if right[i+1]:
                    if not dfs(right[i+1], not flag):
                        return True
                return False
        
        return not dfs(a[0], False)

Explore

在题解中,`字母顺序大于上一个选择的单词`指的是比较两个单词的整个字符串的字典序。这意味着如果在字典中,一个单词(例如'bcd')出现在另一个单词(例如'abc')之后,则第一个单词在字典序上大于第二个单词。题解中的比较操作`left[i] > last`或`right[i] > last`就是执行这样的字典序比较,确保所选的单词在字典序上严格大于上一个选中的单词。

在`dfs`函数中,`not dfs(left[i], not flag)`返回True表示如果当前玩家选择单词`left[i]`后,对手在接下来的回合中没有必胜策略。这是因为`dfs`函数设计为判断当前玩家是否会输掉游戏(即对手有必胜策略)。因此,如果对手的`dfs`调用返回False(表示对手无法必胜),那么`not dfs(...)`就会返回True,显示当前玩家通过选择这个单词能够迫使对手进入一个必败的状态。这正是体现当前玩家拥有必胜策略的关键逻辑。

题解中使用的缓存是通过Python的`@cache`装饰器实现的,它是一种优化技术,用于存储已解决的子问题的结果。在递归过程中,某些状态可能会被重复计算多次,特别是在深度优先搜索中,这会导致效率低下。通过存储已计算过的函数结果,当相同的输入再次出现时,可以直接返回存储的结果,避免重复计算。在这个问题中,缓存存储了每个`(last, flag)`状态的结果,防止在探索不同路径时对相同的游戏状态进行重复的胜负判定。

题解通过将首字母转换为Ascii码偏移量来初始化`left`和`right`数组,这样做可以快速定位到以特定字母开头的单词。这种方法的优点在于它提供了一种快速的索引方式,允许算法直接访问以任何特定字母开头的最大单词,而无需遍历整个单词列表。这种基于首字母索引的方式减少了查找时间,使得算法在处理大量单词时更加高效。此外,这种方法在处理字母开头的排序和比较时也更为直观和方便。