【问题标题】:C++ recursive permutation algorithm for strings -> not skipping duplicates字符串的 C++ 递归排列算法 -> 不跳过重复项
【发布时间】:2011-12-23 05:16:17
【问题描述】:

我很难找到一个简单的语句来跳过此递归排列代码的重复项。我到处寻找,似乎只找到使用交换或 java 的示例。根据我的收集,我认为我需要在 for 循环之后放置一行。

谢谢!

#include "genlib.h"
#include "simpio.h"
#include <string>
#include <iostream>

void ListPermutations(string prefix, string rest);


int main() {

    cout << "Enter some letters to list permutations: ";
    string str = GetLine();
    cout << endl << "The permutations are: " << endl;
    ListPermutations("", str);

    return 0;
}

void ListPermutations(string prefix, string rest)
{
    if (rest == "") 
    {
        cout << prefix << endl;
    } 
    else 
    {   
        for (int i = 0; i < rest.length(); i++) 
        {
            if (prefix != "" && !prefix[i]) continue; // <--- I tried adding this, but it doesn't work
            cout << endl<< "prefix: " << prefix << " | rest: " << rest << endl;     
            string newPrefix = prefix + rest[i];
            string newRest = rest.substr(0, i) + rest.substr(i+1);  
            ListPermutations(newPrefix, newRest);           
        }    
    }
}

【问题讨论】:

  • 我有强烈的感觉,你可以生成它们,这样一开始就不会发出重复的内容。但是,我的大脑现在处于故障状态,我现在似乎无法想象它
  • @sehe - 请参阅下面的答案 - 您只需要在开始之前在 str 上调用 sort ,并且只为每个唯一的 char 递归一次。排序可能甚至没有必要......但我现在不知道如果没有它它是否可以工作

标签: c++ string recursion permutation


【解决方案1】:

这应该有效: 您的算法很好,我只添加了一个测试:如果某个位置已经使用了唯一的字符。如果是,则不再进行排列,因为在该位置具有该字符的所有排列都已进行。

void ListPermutations(string prefix, string rest)
{
if (rest == "") 
{
    cout << prefix << endl;
} 
else 
{   
    for (int i = 0; i < rest.length(); i++) 
    {


        //test if rest[i] is unique.
        bool found = false;
        for (int j = 0; j < i; j++) 
        {
            if (rest[j] == rest[i])
                found = true;
        }
        if(found)
            continue;
        string newPrefix = prefix + rest[i];
        string newRest = rest.substr(0, i) + rest.substr(i+1);  
        ListPermutations(newPrefix, newRest);           
    }    
}
}

你也可以在排列前对字符串进行排序,结果是一样的。

【讨论】:

  • 非常感谢,太棒了,我从来没有想过要添加另一个 for 循环...!
【解决方案2】:

在 C++ 中使用 std::next_permutation 生成排列

它会很好地处理重复条目并做正确的事情

【讨论】:

  • 谢谢,但还有其他方法使用布尔逻辑或其他方法吗?谢谢,很抱歉没有早点指定作业。
  • 要跳过重复项,您可能必须修改代码以维护每个字符及其当前计数。递归函数将使用一个字符并降低其计数并调用自身。
【解决方案3】:

忽略 std::next_permutation 的可用性,因为您对上一个答案的评论...

如果您想生成所有 唯一 排列,则需要在某个时候将它们按顺序排列。最简单的方法是将它们全部放在一个向量中,对其进行排序,然后在打印出来时抑制重复的相邻条目。但这比它需要的要慢得多。

您需要从对字符串进行排序开始,以便在彼此之后生成相同的排列。然后在你的 for 循环中,确保跳过“rest”中的任何重复字母。类似:

    char lastAdditionToPrefix = '\0';
    for (int i = 0; i < rest.length(); i++) 
    {
        if (rest[i] == lastAdditionToPrefix) continue;
        lastAdditionToPrefix = rest[i];
        cout << endl<< "prefix: " << prefix << " | rest: " << rest << endl;     
        ...

我不相信这个改变会完全修复你的代码,但它比你现在更接近。编辑:这个,加上在 main() 中对输入进行排序,将起作用

【讨论】:

  • 谢谢,vector 确实会慢一些。我的感觉是它与比较前缀与休息有关并跳过下一个递归或增量 i?当我试图解决它时,我的大脑会爆炸......
  • 我认为你根本不需要比较前缀和休息 - 你只需要确保你的函数只为“休息”中的每个唯一字符调用一次。我的答案中的代码应该这样做,因为如果初始字符串已排序(在您的示例中没有,您需要在 main 中的 str 上调用 std::sort ,则将始终对“rest”进行排序)跨度>
【解决方案4】:

经过测试,工作正常。这个想法是针对每个堆栈级别,在位置 i,记住一个角色是否已经在该位置。

using namespace std;

void doPermutation(vector<char> &input, int index) {
    bool used[26] = {false};

    if(index == input.size()) {
        copy(input.begin(), input.end(), ostream_iterator<char>(cout, "") );
        cout << endl;

    } else {
      int i, j;
      for(i =index; i < input.size(); i++ ) {
        if(used[ input[i]-'a'] == false) {
           swap(input[i], input[index]);
           doPermutation(input, index+1);
           swap(input[i], input[index]);
           used[input[i]-'a'] = true;
       } 
      }
    }
}

  void permutation(vector<char>& input) {
      doPermutation(input, 0);
  }


int main()
{
   cout << "Hello World" << endl; 
   const char* inp = "alla";
   vector<char> input(inp, inp + 4 );

   permutation(input);

   return 0;
}

【讨论】:

    【解决方案5】:

    有或没有重复的算法的不同之处在于当你交换它时,确保你想要交换的字符之前没有被交换过。使用哈希映射来跟踪您之前交换过的内容。

    请参阅下面的 C# 代码。

        private void PermuteUniqueHelper(int[] nums, int index, List<IList<int>> res)
        {
            var used = new Dictionary<int, bool>();
            if (index == nums.Length)
            {
                res.Add(new List<int>(nums));
                return;
            }
    
            for (int i = index; i < nums.Length; i++)
            {
                if (!used.ContainsKey(nums[i]))
                {
                    swap(nums[i], nums[index]);
    
                    this.PermuteUniqueHelper(nums, index + 1, res);
    
                    swap(nums[i], nums[index]);
    
                    used.Add(nums[i], true);
                }
            }
        }
    

    【讨论】:

      猜你喜欢
      • 2011-11-22
      • 2014-12-03
      • 2015-02-20
      • 2012-09-30
      • 1970-01-01
      • 2011-04-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多