字符串的排列需要很长时间才能解决

时间:2017-05-17 20:32:15

标签: ruby algorithm

我正在创建一个字符串中的排列和唯一字母数组,只是按字母顺序排序并找到集合中的中间元素。

def middle_permutation(string)
  length = string.length
  permutation_set = string.split("").permutation(length).to_a.map{|item| item.join}.sort
  permutation_set.length.even? ? permutation_set[(permutation_set.length)/2-1] : permutation_set[(permutation_set.length/2)+1]
end

例如:

middle_permutation("zxcvbnmasd") should equal "mzxvsndcba" 

即使对于小字符串(N> = 10),计算也需要很长时间才能完成,而且我可以忘记任何双重字符串;有更快的方法吗?

5 个答案:

答案 0 :(得分:1)

我假设这些字母是唯一的,就像OP的问题一样。

  1. 排序
  2. 插入已排序字符串的中间字母(向下舍入)。这是中间排列的第一个字母。
  3. 如果原始列表的字母数为偶数,则排列的其余部分与其余字母相反。
  4. 如果没有,请再次拿中间字母。现在结果的其余部分是其余字母的反向排列。

答案 1 :(得分:1)

下面的方法直接返回所需的排列,而不迭代排列。

提问者声明该字符串不包含重复的字母,这是此方法的要求。我假设字符串的字符已排序。如果不是,则创建排序字符串将是第一步:

str = "ebadc".chars.sort.join
  #=> "abcde"

<强>代码

def mid_perm(str)
  return mid_perm_even_length_strings(str) if str.size.even?
  first_char_index = str.size/2
  str[first_char_index] << mid_perm_even_length_strings(str[0,first_char_index] +
    str[first_char_index+1..-1])
end

def mid_perm_even_length_strings(str)
  first_char_index = str.size/2-1
  str[first_char_index] + (str[0,first_char_index] + str[first_char_index+1..-1]).reverse
end

<强>实施例

mid_perm 'abcd'
  #=> "bdca" 
mid_perm 'abcde'
  #=> "cbeda" 
mid_perm 'abcdefghijklmnopqrstuvwxyz'
  #=> "mzyxwvutsrqponlkjihgfedcba" 

<强>解释

让我们首先定义一个生成字符串字母排列的方法。

def perms(str)
  str.chars.permutation(str.size).map(&:join)
end

包含偶数个字符的字符串

考虑

a = perms "abcd"
  #=> ["abcd", "abdc", "acbd", "acdb", "adbc", "adcb",
  #    "bacd", "badc", "bcad", "bcda", "bdac", "bdca",
  #    "cabd", "cadb", "cbad", "cbda", "cdab", "cdba",
  #    "dabc", "dacb", "dbac", "dbca", "dcab", "dcba"] 

a包含4! #=> 4*3*2 => 24个元素,4是字符串的长度。

请注意,由于perms'参数中的字符已排序,因此返回的数组也按 1 排序。

a == a.sort  #=>true

作为a.size #=> 24,“中间”元素为a[11] #=> "bdca"a[12] #=> "cabd"(其中11 = (24-1)/212 = 24/2),具体取决于我们想要舍入的方式。这个问题规定,对于偶数长度的字符串,我们要向下舍入,这样就是"bdca"

现在让我们将a切成str.size个相等的数组,每个数组包含a.size/str.size #=> 24/4 => 6个元素:

b = a.each_slice(a.size/str.size).to_a
  #=> [["abcd", "abdc", "acbd", "acdb", "adbc", "adcb"],
  #    ["bacd", "badc", "bcad", "bcda", "bdac", "bdca"],
  #    ["cabd", "cadb", "cbad", "cbda", "cdab", "cdba"],
  #    ["dabc", "dacb", "dbac", "dbca", "dcab", "dcba"]]

因此,所需的元素是

b[(a.size/str.size-1)/2-1][-1]
  #=> "bdca"

此值可以更直接地计算如下。

first_char_index = str.size/2-1
  #=> 1 
first_char = str[first_char_index]
  #=> "b" 
remaining_chars = (str[0,first_char_index] + str[first_char_index+1..-1]).reverse
  #=> "dca" 
first_char + remaining_chars
  #=> "bdca" 

相同的逻辑适用于具有偶数个字符的所有字符串。因此,我们可以编写上面代码部分中显示的方法mid_perm_even_length_strings

例如(对于12个字符的字符串)

mid_perm_even_length_strings 'abcdefghijkl'
  #=> "flkjihgedcba"

包含奇数个字符的字符串

现在考虑

str = "abcde"
a = perms str
  #=> ["abcde", "abced", "abdce", "abdec", "abecd", "abedc",
  #    "acbde", "acbed", "acdbe", "acdeb", "acebd", "acedb",
  #    "adbce", "adbec", "adcbe", "adceb", "adebc", "adecb",
  #    "aebcd", "aebdc", "aecbd", "aecdb", "aedbc", "aedcb",

  #    "bacde", "baced", "badce", "badec", "baecd",..., "bedca",

  #    "cabde", "cabed", "cadbe", "cadeb", "caebd", "caedb",
  #    "cbade", "cbaed", "cbdae", "cbdea", "cbead", "cbeda",
  #    "cdabe", "cdaeb", "cdbae", "cdbea", "cdeab", "cdeba",
  #    "ceabd", "ceadb", "cebad", "cebda", "cedab", "cedba",

  #   "dabce", "dabec", "dacbe", "daceb", "daebc",..., "decba",

  #   "eabcd", "eabdc", "eacbd", "eacdb", "eadbc",..., "edcba"] 

此处排列包含5! #=> 1005 20a.each_cons(2).all? { |s1,s2| s1 < s2 } #=> true个元素。 (再次,a。)

str[str.size/2] #=> "c" 的中间元素显然是以

开头的元素块的中间元素
b = a.each_slice(a.size/str.size).to_a[str.size/2]
  #=> ["cabde", "cabed", "cadbe", "cadeb", "caebd", "caedb",
  #    "cbade", "cbaed", "cbdae", "cbdea", "cbead", "cbeda",
  #    "cdabe", "cdaeb", "cdbae", "cdbea", "cdeab", "cdeba",
  #    "ceabd", "ceadb", "cebad", "cebda", "cedab", "cedba"]

该块将是数组

'c'

这将是["abde", "abed", "adbe", "adeb", "aebd", "aedb", "bade", "baed", "bdae", "bdea", "bead", "beda", "dabe", "daeb", "dbae", "dbea", "deab", "deba", "eabd", "eadb", "ebad", "ebda", "edab", "edba"] 加上数组的中间元素

"abde"

该数组仅仅是字符串mid_perm_even_length_strings 'abde' #=> "beda" 的排列。由于该字符串包含偶数字符,因此其中间元素为

"abcde"

因此,'c' + 'abde' #=> "cabde" 字母排列的中间元素是

.sort

这显然适用于包含奇数个字符的所有字符串。

1。 Array#permutation的文档指出,“实现不能保证排列的顺序。”因此,我们可能需要将perms添加到document.getElementById("element")的操作行的末尾,但是使用Ruby v2.4(我怀疑,早期版本),实际上这里没有必要

答案 2 :(得分:0)

我能够像这样压缩它:

def middle_permutation(string)
  list = string.chars.permutation.map(&:join).sort

  list[list.length / 2 - (list.length.even? ? 1 : 0)]
end

哪个收益率:

middle_permutation('zxcvbnmasd')
# => "mzxvsndcba"

答案 3 :(得分:0)

事实证明,有两条曲目,奇数字符串甚至字符串。

对于奇数字符串,按顺序取出排序数组的中间字符元素和前面的元素。当你这样做时,你有两个剩余的阵列,左右一个,按字母顺序排序。您可以使用最后一个元素从右侧数组的元素开始,然后对左侧的元素执行相同操作。

对于偶数字符串,请执行相同但仅在第一步中使用一个字符:(N / 2)元素。

这是我的解决方案:

def middle_permutation(string)
  string_array = string.chars.sort

  mid_string = []
  length = string.length
    if length.even? 
      mid_string << string_array[length/2-1]
      string_array.delete_at(length/2-1)
      (mid_string << string_array.reverse).flatten.join
    else 
      mid_string << string_array[(length/2)-1..length/2].reverse
      string_array.slice!((length/2)-1, 2)
     (mid_string << string_array.reverse).flatten.join
    end
  end

答案 4 :(得分:0)

您无需生成所有排列。只需查找排列的总数为PN = N!,其中N是字符串(不同字符)长度,并按其编号仅计算所需的PN/2-th排列 - 例如,使用this approach

     public static int[] perm(int n, int k)  
 {  
      int i, ind, m=k;  
      int[] permuted = new int[n];  
      int[] elems = new int[n];  
      for(i=0;i<n;i++) elems[i]=i;  
      for(i=0;i<n;i++)  
      {  
           ind=m%(n-i);  
           m=m/(n-i);  
           permuted[i]=elems[ind];  
           elems[ind]=elems[n-i-1];  
      }  
      return permuted;  
 }