Java 单词数组中的回文对

Java 单词数组中的回文对,java,algorithm,palindrome,Java,Algorithm,Palindrome,我遇到了一个回文对问题的代码,使用Trie public static class Trie { int pos; Trie[] nodes; // consider xyxabc. if current trie is 'a'. Then a.nodes has information. It means string after a is palindrome List<Integer> palins; public Trie() {

我遇到了一个回文对问题的代码,使用Trie

public static class Trie {
    int pos;
    Trie[] nodes;   // consider xyxabc. if current trie is 'a'. Then a.nodes has information. It means string after a is palindrome
    List<Integer> palins;
    public Trie() {
        pos = -1;
        nodes = new Trie[26];
        palins = new ArrayList<>();
    }
}

public static void add(Trie root, String word, int pos) {
    for (int i = word.length() - 1; i >= 0; i--) {
        char ch = word.charAt(i);
        if (isPalindrome(word, 0, i)) { // check if substring(0, i) is palindrome.
            root.palins.add(pos);
        }
        if (root.nodes[ch - 'a'] == null) {
            root.nodes[ch - 'a'] = new Trie();
        }
        root = root.nodes[ch - 'a'];
    }
    root.pos = pos; // if it is xyxcba. Until now, the node should be at x.
    root.palins.add(pos);
}

public static void search(Trie root, String[] words, int i, List<List<Integer>> ans) {
    int len = words[i].length();
    for (int j = 0; j < len && root != null; j++) {
        if (root.pos >= 0 && i != root.pos && isPalindrome(words[i], j, len - 1)) {
            ans.add(Arrays.asList(new Integer[] {i, root.pos}));
        }
        char ch = words[i].charAt(j);
        root = root.nodes[ch - 'a'];
    }
    if (root != null && root.palins.size() > 0) { // assume 'xyxabc' is in trie, now try 'cba'
        for (int j : root.palins) {
            if (j != i) {
                ans.add(Arrays.asList(new Integer[] {i, j}));
            }
        }
    }
}

public static List<List<Integer>> palindromePairs(String[] words) {
    List<List<Integer>> ans = new ArrayList<>();
    Trie trie = new Trie();
    for (int i = 0; i < words.length; i++) {
        add(trie, words[i], i);
    }
    for (int i = 0; i < words.length; i++) {
        search(trie, words, i, ans);
    }
    return ans;
}

public static boolean isPalindrome(String str, int i, int j) {
    while (i < j) {
        if (str.charAt(i++) != str.charAt(j--)) {
            return false;
        }
    }
    return true;
}
在FOR循环之外,我们为什么需要添加:

root.palins.add(pos);
我在这里看到了这段代码:

然而,我仍然发现很难理解这种方法


谢谢

我认为您链接到的博客文章没有很好地解释解决方案的核心思想。这可能使代码难以理解

让我们从问题开始(因为你没有引用它):

给出了一个独特的单词列表。在给定列表中查找所有不同索引对
(i,j)
,以便两个单词的串联,即
单词[i]+单词[j]
是回文

示例1:给定的
words=[“bat”、“tab”、“cat”]

返回
[[0,1],[1,0]]

回文是
[“battab”,“tabbat”]

示例2:给定的
单词=[“abcd”、“dcba”、“lls”、“s”、“sssll”]

返回
[[0,1],[1,0],[3,2],[2,4]
回文是
[“dcbaabcd”、“abcddcba”、“slls”、“llssssll”]

两个单词
words[i]
words[j]
何时满足
words[i]+words[j]
成为回文的条件

  • 如果
    单词[i]
    单词[j]
    长度相等,则
    单词[i]
    必须等于
    单词[j]
    的倒数(反之亦然)
  • 如果其中一个单词(例如,
    words[i]
    )较长,那么它必须以
    words[j]
    的反面结尾,其余的
    words[i]
    必须是回文。
    例如,以
    lls
    s
    为例
    lls
    s
    结尾,
    ll
    是回文。所以
    s
    +
    lls
    是回文
注意到这一点,我们基本上需要一种有效的方法来检查一些
单词[i]
是否以
单词[j]
的相反结尾,以及
单词[i]
的其余部分是否是回文

这是使用
Trie
结构实现的,该结构在反转的字上构建基数树。这篇博客文章给出了一个很好的例子,展示了单词“cbaaa”、“bc”、“abc”的构建
Trie

这个
Trie
使得搜索前缀非常有效。例如,搜索
cba
(与
abc
相反)会让节点开始
cbaaa

我认为理解代码的主要问题是由于字段名称不正确(代码通常有点混乱)。不清楚什么是
pos
palins
,因此这可能会给您带来麻烦

给定
Trie
节点的
pos
只是从“根”到给定节点的节点序列形成的单词索引。在上面的示例中,
cbaaa
具有索引
0
bc
索引
1
abc
索引
2
。相应的节点具有适当的
pos

佩林更有趣一点。在将
Trie
构建到某个节点时,我们有一些公共前缀。例如,当
Trie
构建到
c-b-a-a-a
中的节点
a
时,我们有公共前缀
cba
。但我们还需要快速检查的是单词的其余部分是回文。这就是佩林的用意
palins
保存以我们目前在构造中使用的通用前缀开头的单词索引,其中单词的其余部分是回文。在我们的示例中,
cbaaa
cba
开头,其余的
aa
是回文。因此
words
cbaaa
的索引
0
将添加到
palins
。如果有类似于
cbatt
,它的索引也会添加到
palins

我们还需要考虑诸如“代码> ABC <代码> ->代码> CBA <代码>的情况,这里没有余数。通过将单词索引添加到最后一个符号节点的
palins
(这就是为什么您会看到另一个
root.palins.add(pos);
for
循环之外)

现在应该大致清楚搜索方法的工作原理: *对于每个单词,搜索与该单词相同的前缀。如果有这样的前缀,则
Trie
中会有相应的节点。 *如果找到这样的节点,请检查
palins
。这将有效地给出给定单词的回文对索引

例如,当我们搜索
abc
时,我们将在
c-b-a-a-a
中找到第一个
a
节点。前缀
cba
abc
相反。这个
a
节点的
palins
将包含
0
cbaaa
的索引位于
words
),因此我们将得到
[2,0]
作为结果之一

root.palins.add(pos);