这道题的核心思路是逆序字典树(Trie)——因为要找的是最长公共后缀,所以把字符串逆序插入 Trie,后缀就变成了前缀,问题就转化为经典的 Trie 前缀匹配问题。

题目思路

关键观察
- 后缀匹配 → 逆序后变成前缀匹配
- 每个 Trie 节点需要记录:当前路径下最短字符串的长度及其索引
- 如果多个字符串长度相同,取最早出现的索引

破平规则
1. 最长公共后缀优先
2. 后缀长度相同 → 字符串长度最短优先
3. 长度也相同 → 出现索引最早优先

Java 实现

```java
class Solution {
    
    // Trie 节点
    private static class TrieNode {
        TrieNode[] children = new TrieNode[26];
        // 当前节点对应的最短字符串长度
        int minLen = Integer.MAX_VALUE;
        // 当前节点对应的最短字符串的索引
        int idx = -1;
    }
    
    private TrieNode root = new TrieNode();
    
    // 更新节点信息:如果新字符串更短,或长度相同但索引更早
    private void update(TrieNode node, int len, int index) {
        if (len < node.minLen) {
            node.minLen = len;
            node.idx = index;
        }
    }
    
    // 逆序插入字符串到 Trie
    private void insert(String word, int index) {
        int len = word.length();
        TrieNode node = root;
        
        // 根节点也要更新(对应空后缀 "")
        update(node, len, index);
        
        // 逆序遍历,从最后一个字符开始
        for (int i = len - 1; i >= 0; i--) {
            int c = word.charAt(i) - 'a';
            if (node.children[c] == null) {
                node.children[c] = new TrieNode();
            }
            node = node.children[c];
            update(node, len, index);
        }
    }
    
    // 查询最长公共后缀对应的索引
    private int query(String word) {
        TrieNode node = root;
        
        // 逆序遍历查询字符串
        for (int i = word.length() - 1; i >= 0; i--) {
            int c = word.charAt(i) - 'a';
            // 如果当前字符路径不存在,说明最长公共后缀到此为止
            if (node.children[c] == null) {
                break;
            }
            node = node.children[c];
        }
        
        return node.idx;
    }
    
    public int[] stringIndices(String[] wordsContainer, String[] wordsQuery) {
        // 构建逆序 Trie
        for (int i = 0; i < wordsContainer.length; i++) {
            insert(wordsContainer[i], i);
        }
        
        int n = wordsQuery.length;
        int[] ans = new int[n];
        
        // 处理每个查询
        for (int i = 0; i < n; i++) {
            ans[i] = query(wordsQuery[i]);
        }
        
        return ans;
    }
}
```

复杂度分析

指标    复杂度    
时间    O(Σ\|wordsContainer[i]\| + Σ\|wordsQuery[i]\|),即所有字符总数    
空间    O(Σ\|wordsContainer[i]\| × 26),Trie 节点数    

关键点总结

1. 逆序插入:将后缀问题转化为前缀问题,这是 Trie 处理后缀的标准技巧
2. 节点信息维护:每个节点记录当前路径下最短字符串的长度和索引,保证查询时能直接返回最优解
3. 根节点处理:根节点对应空后缀 `""`,需要正确初始化(取所有字符串中最短的)
4. 破平顺序:通过 `len < node.minLen` 的条件判断,自然实现了"更短优先、更早优先"的破平规则

示例验证

输入:`wordsContainer = ["abcd","bcd","xbcd"]`, `wordsQuery = ["cd","bcd","xyz"]`

- 逆序插入:`"dcba"`、`"dcb"`、`"dcbx"`
- 查询 `"cd"`(逆序 `"dc"`):匹配到 `"dcb"` 的 `"dc"` 路径,返回索引 1
- 查询 `"bcd"`(逆序 `"dcb"`):完全匹配 `"dcb"`,返回索引 1
- 查询 `"xyz"`(逆序 `"zyx"`):无匹配,返回根节点的最短字符串索引 1

输出:`[1, 1, 1]`

 

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐