算法:按字典顺序在给定索引处查找给定字符串的字谜

Posted

技术标签:

【中文标题】算法:按字典顺序在给定索引处查找给定字符串的字谜【英文标题】:Algo: Find anagram of given string at a given index in lexicographically sorted order 【发布时间】:2017-11-12 07:41:35 【问题描述】:

需要编写一个算法以按字典顺序在给定索引处查找给定字符串的 Anagram。例如:

考虑一个字符串:ABC,那么所有字谜都按排序顺序排列:ABC ACB BAC BCA CAB CBA。因此,对于 索引 5 的值是:CAB。此外,考虑 重复 的情况,例如 AADFS anagram 将是索引 32 处的 DFASA

为此,我编写了 Algo,但我认为应该有比这更简单的东西。

import java.util.*;
public class Anagram 

static class Word 
    Character c;
    int count;

    Word(Character c, int count) 
        this.c = c;
        this.count = count;
    


public static void main(String[] args) 
    System.out.println(findAnagram("aadfs", 32));



private static String findAnagram(String word, int index) 
    // starting with 0 that's y.
    index--;

    char[] array = word.toCharArray();
    List<Character> chars = new ArrayList<>();
    for (int i = 0; i < array.length; i++) 
        chars.add(array[i]);
    
    // Sort List
    Collections.sort(chars);

    // To maintain duplicates
    List<Word> words = new ArrayList<>();
    Character temp = chars.get(0);
    int count = 1;
    int total = chars.size();
    for (int i = 1; i < chars.size(); i++) 
        if (temp == chars.get(i)) 
            count++;
         else 
            words.add(new Word(temp, count));
            count = 1;
            temp = chars.get(i);
        
    
    words.add(new Word(temp, count));

    String anagram = "";
    while (index > 0) 
        Word selectedWord = null;
        // find best index
        int value = 0;
        for (int i = 0; i < words.size(); i++) 
            int com = combination(words, i, total);
            if (index < value + com) 
                index -= value;
                if (words.get(i).count == 1) 
                    selectedWord = words.remove(i);
                 else 
                    words.get(i).count--;
                    selectedWord = words.get(i);
                
                break;
            
            value += com;
        
        anagram += selectedWord.c;
        total--;
    
    // put remaining in series
    for (int i = 0; i < words.size(); i++) 
        for (int j = 0; j < words.get(i).count; j++) 
            anagram += words.get(i).c;
        
    
    return anagram;


private static int combination(List<Word> words, int index, int total) 
    int value = permutation(total - 1);
    for (int i = 0; i < words.size(); i++) 
        if (i == index) 
            int v = words.get(i).count - 1;
            if (v > 0) 
                value /= permutation(v);
            
         else 
            value /= permutation(words.get(i).count);
        
    
    return value;


private static int permutation(int i) 
    if (i == 1) 
        return 1;
    
    return i * permutation(i - 1);

谁能帮我解决不太复杂的逻辑。

【问题讨论】:

好问题。没有想太多,你的代码对我来说看起来并不过分复杂。 我遇到了同样的问题。这是editorial 的链接。希望这会有所帮助。 使用带有“排列”和“排名”的搜索功能。 【参考方案1】:

我编写以下代码来解决您的问题。

我假设给定的字符串是排序的。

permutations(String prefix, char[] word, ArrayList permutations_list) 函数生成给定字符串的所有可能排列而不重复,并将它们存储在名为 permutations_list。因此,单词:permutations_list.get(index -1) 是所需的输出。

例如,假设有人给了我们“aab”这个词。 我们必须递归地解决这个问题:

问题 1:排列(“”,“aab”)。

这意味着我们必须解决问题:

问题 2:排列(“a”,“ab”)。 字符串“ab”只有两个字母,因此可能的排列是“ab”和“ba”。因此,我们将单词“aab”和“aba”存储在 permutations_list 中。

问题 2 已解决。现在我们回到问题 1。 我们交换第一个“a”和第二个“a”,我们意识到这些字母是相同的。所以我们跳过这种情况(我们避免重复)。 接下来,我们交换第一个“a”和“b”。现在,问题 1 发生了变化,我们要解决新的问题:

问题 3:排列(“”,“baa”)。

下一步是解决以下问题:

问题 4:排列(“b”,“aa”)。 字符串“aa”只有两个相同的字母,因此有一个可能的排列“aa”。因此,我们在 permutations_list 中存储单词“baa”

问题 4 已解决。最后,我们回到问题 3,问题 3 已经解决了。最终的 permutations_list 包含“aab”、“aba”和“baa”。

因此,findAnagram("aab", 2) 返回单词“aba”。

import java.util.ArrayList;
import java.util.Arrays;

public class AnagramProblem 

 public static void main(String args[]) 
     System.out.println(findAnagram("aadfs",32));
 

 public static String findAnagram(String word, int index) 
     ArrayList<String> permutations_list = new ArrayList<String>();
     permutations("",word.toCharArray(), permutations_list);
     return permutations_list.get(index - 1);
  

 public static void permutations(String prefix, char[] word, ArrayList<String> permutations_list) 

    boolean duplicate = false;

    if (word.length==2 && word[0]!=word[1]) 
        String permutation1 = prefix + String.valueOf(word[0]) + String.valueOf(word[1]);
        permutations_list.add(permutation1);
        String permutation2 = prefix + String.valueOf(word[1]) + String.valueOf(word[0]);
        permutations_list.add(permutation2);
        return;
    
    else if (word.length==2 && word[0]==word[1]) 
        String permutation = prefix + String.valueOf(word[0]) + String.valueOf(word[1]);
        permutations_list.add(permutation);
        return;
    

    for (int i=0; i < word.length; i++) 
        if (!duplicate) 
            permutations(prefix + word[0], new String(word).substring(1,word.length).toCharArray(), permutations_list);
        
        if (i < word.length - 1) 
            char temp = word[0];
            word[0] = word[i+1];
            word[i+1] = temp;
        
        if (i < word.length - 1 && word[0]==word[i+1]) duplicate = true;
        else duplicate = false;
    


 





【讨论】:

【参考方案2】:

如果您考虑按字母顺序生成字谜,我认为您的问题会变得简单得多,因此您不必事后对它们进行排序。

以下代码(来自Generating all permutations of a given string)生成字符串的所有排列。这些排列的顺序由输入字符串的初始顺序给出。如果您事先对字符串进行排序,则字谜将因此按排序顺序添加。

为了防止重复,您可以简单地维护一组您已经添加的字符串。如果此 Set 不包含您要添加的字谜,那么您可以安全地将其添加到字谜列表中。

这是我描述的解决方案的代码。我希望您发现它比您的解决方案更简单。

public class Anagrams 

private List<String> sortedAnagrams;
private Set<String> handledStrings;

public static void main(String args[]) 
    Anagrams anagrams = new Anagrams();
    List<String> list = anagrams.permutations(sort("AASDF"));
    System.out.println(list.get(31));


public List<String> permutations(String str) 
    handledStrings = new HashSet<String>();
    sortedAnagrams = new ArrayList<String>();
    permutation("", str);
    return sortedAnagrams;


private void permutation(String prefix, String str) 
    int n = str.length();
    if (n == 0)
        if(! handledStrings.contains(prefix))
            //System.out.println(prefix);
            sortedAnagrams.add(prefix);
            handledStrings.add(prefix);
        
    
    else 
        for (int i = 0; i < n; i++)
            permutation(prefix + str.charAt(i), str.substring(0, i) + str.substring(i + 1, n));
    


public static String sort(String str) 
    char[] arr = str.toCharArray();
    Arrays.sort(arr);
    return new String(arr);



【讨论】:

【参考方案3】:

如果您创建一个“下一个排列”方法将数组更改为其下一个字典排列,那么您的基本逻辑可能是在循环中调用该方法n-1 次。

可以在here 找到一个很好的代码描述。这是从该页面改编而来的基本伪代码和 Java 示例。

/*
1. Find largest index i such that array[i − 1] < array[i].
   (If no such i exists, then this is already the last permutation.)
2. Find largest index j such that j ≥ i and array[j] > array[i − 1].
3. Swap array[j] and array[i − 1].
4. Reverse the suffix starting at array[i].
*/

boolean nextPermutation(char[] array) 
    int i = array.length - 1;
    while (i > 0 && array[i - 1] >= array[i]) i--;
    if (i <= 0) return false;
    int j = array.length - 1;
    while (array[j] <= array[i - 1]) j--;
    char temp = array[i - 1];
    array[i - 1] = array[j];
    array[j] = temp;
    j = array.length - 1;
    while (i < j) 
        temp = array[i];
        array[i] = array[j];
        array[j] = temp;
        i++;
        j--;
    
    return true;

【讨论】:

以上是关于算法:按字典顺序在给定索引处查找给定字符串的字谜的主要内容,如果未能解决你的问题,请参考以下文章

给定一些文本输入,找到最大的字谜组

从单词列表中查找给定句子的字谜

在给定的字符串列表中查找字符串的所有字谜

查找给定单词的字谜

查找字符串中的所有字谜如何优化

查找给定数字的下一个较大的字谜