字符串排列排名+数据结构

时间:2012-07-14 09:48:54

标签: string algorithm data-structures permutation

手头的问题是:

  

给出一个字符串。在排序的所有排列中说出它的排名   字典顺序。

可以用数学方法尝试这个问题,但我想知道是否还有其他一些算法来计算它?

此外,如果我们必须按顺序存储所有字符串排列,我们如何有效地生成它们(以及复杂性)。什么是用于存储排列的良好的数据结构,并且对于检索也是有效的?

编辑

感谢关于排列生成部分的详细解答,有人还可以建议一个良好的数据结构吗?我只能想到树木。

3 个答案:

答案 0 :(得分:6)

有一种O(n |Σ|)算法可以在其排列列表中找到长度为n的字符串的等级。这里,Σ是字母表。

算法

排名低于 s 的每个排列都可以 pcx 的形式唯一写入;其中:

  • p s
  • 的正确前缀
  • c 是排在 s p 之后的角色以下的角色。并且 c 也是 s 中未包含在 p 部分的字符。
  • x s 中剩余字符的任何排列;即不包括在 p c

我们可以通过以增加的长度顺序迭代 s 的每个前缀来计算每个类中包含的排列,同时保持字符的频率出现在的剩余部分中s ,以及 x 表示的排列数。详细信息留待读者阅读。

这假设所涉及的算术运算需要恒定的时间;它不会;因为涉及的数字可以有nlog |Σ|数字。考虑到这一点,该算法将在O(n 2 log |Σ| log(nlog |Σ|))中运行。因为我们可以在O(dlogd)中添加,减去,乘以和除以两个d位数字。

C ++实施

typedef long long int lli;

lli rank(string s){
    int n = s.length();

    vector<lli> factorial(n+1,1);
    for(int i = 1; i <= n; i++)
        factorial[i] = i * factorial[i-1];

    vector<int> freq(26);
    lli den = 1;
    lli ret = 0;
    for(int i = n-1; i >= 0; i--){
        int si = s[i]-'a';
        freq[si]++;
        den *= freq[si];
        for(int c = 0; c < si; c++) 
            if(freq[c] > 0) 
                ret += factorial[n-i-1] / (den / freq[c]);
    }
    return ret + 1;
}

答案 1 :(得分:3)

这类似于quickselect algorithm。在未排序的整数数组中,找到某个特定数组元素的索引。分区元素将是给定的字符串。

编辑:

实际上它类似于在QuickSort中完成的分区方法。给定的字符串是分区元素。一旦生成了所有排列,找到长度为k的字符串的排名的复杂度将是O(nk)。您可以使用递归生成字符串排列并将它们存储在链接列表中。您可以将此链接列表传递给分区方法。

这是生成所有String排列的java代码:

 private static int generateStringPermutations(String name,int currIndex) {

        int sum = 0;

        for(int j=name.length()-1;j>=0;j--) {
            for(int i=j-1;((i<j) && (i>currIndex));i--) {

                String swappedString = swapCharsInString(name,i,j);
                list.add(swappedString);
                //System.out.println(swappedString);
                sum++;
                sum = sum + generateStringPermutations(swappedString,i);
            }
        }
        return sum;


    }

编辑:

生成所有排列代价很高。如果字符串包含不同的字符,则可以在不生成所有排列的情况下确定排名。 Here's the link

这可以针对存在重复字符的情况进行扩展。

而不是x *(n-1)!这是针对链接中提到的不同情况,

对于重复的字符,它将是:

如果有1个字符重复两次,

x *(n-1)!/ 2!

我们举一个例子。对于字符串abca,组合是:

aabc,aacb,abac,abca,acab,acba,baac,baca, bcaa ,caab, caba ,cbaa(按排序顺序)

总组合= 4!/ 2! = 12

如果我们想要找到'bcaa'的等级,那么我们知道以'a'开头的所有字符串都在3之前! = 6.

请注意,因为'a'是起始字符,剩下的字符是a,b,c,并且没有重复,所以它是3!。我们也知道以'ba'开头的字符串将在2之前! = 2所以排名是9

另一个例子。如果我们想要找到'caba'的等级

以a开头的所有字符串都在= 6之前。 所有以b开头的字符串都在= 3!/ 2之前! = 3(因为一旦我们选择了b,我们就会留下a,a,c,因为重复是3!/ 2! 所有以caa开头的字符串都将是1

所以最终排名为11

答案 2 :(得分:1)

来自GeeksforGeeks

  

给定一个字符串,在排序的所有排列中找到它的排名   字典顺序。例如,“abc”的等级是1,“acb”的等级是   2,“cba”的等级是6。

     

为简单起见,我们假设该字符串不包含任何字符串   重复的字符。

     

一个简单的解决方案是将rank初始化为1,生成所有   词典顺序排列。生成排列后,   检查生成的排列是否与给定的字符串相同,如果相同,   然后返回等级,如果没有,则将等级增加1.时间   在最坏的情况下,这种解决方案的复杂性将是指数级的。   以下是一种有效的解决方案。

     

让给定的字符串为“STRING”。在输入字符串中,'S'是   第一个角色。总共有6个字符,其中4个是   小于'S'。所以可以有4 * 5!首先是较小的字符串   字符小于'S',如下面的

     

R X X X X X I X X X X X X X X X X G X X X X X

     

现在让我们修复S'并找到用'S'盯着的较小字符串。

     

对T重复相同的过程,等级为4 * 5! + 4 * 4! + ...

     

现在修复T并为R重复相同的过程,等级为4 * 5! + 4 * 4! +   3 * 3! + ...

     

现在修复R并为我重复相同的过程,等级为4 * 5! + 4 * 4! +   3 * 3! + 1 * 2! + ...

     

现在修复我并为N重复相同的过程,等级为4 * 5! + 4 * 4! +   3 * 3! + 1 * 2! + 1 * 1! + ...

     

现在修复N并为G重复相同的过程,等级为4 * 5! + 4 * 4 + 3 * 3!   + 1 * 2! + 1 * 1! + 0 * 0!

     

等级= 4 * 5! + 4 * 4! + 3 * 3! + 1 * 2! + 1 * 1! + 0 * 0! = 597

     

由于等级的值从1开始,最终等级= 1 + 597 = 598