手头的问题是:
给出一个字符串。在排序的所有排列中说出它的排名 字典顺序。
可以用数学方法尝试这个问题,但我想知道是否还有其他一些算法来计算它?
此外,如果我们必须按顺序存储所有字符串排列,我们如何有效地生成它们(以及复杂性)。什么是用于存储排列的良好的数据结构,并且对于检索也是有效的?
编辑
感谢关于排列生成部分的详细解答,有人还可以建议一个良好的数据结构吗?我只能想到树木。
答案 0 :(得分:6)
有一种O(n |Σ|)算法可以在其排列列表中找到长度为n的字符串的等级。这里,Σ是字母表。
排名低于 s 的每个排列都可以 pcx 的形式唯一写入;其中:
我们可以通过以增加的长度顺序迭代 s 的每个前缀来计算每个类中包含的排列,同时保持字符的频率出现在的剩余部分中s ,以及 x 表示的排列数。详细信息留待读者阅读。
这假设所涉及的算术运算需要恒定的时间;它不会;因为涉及的数字可以有nlog |Σ|数字。考虑到这一点,该算法将在O(n 2 log |Σ| log(nlog |Σ|))中运行。因为我们可以在O(dlogd)中添加,减去,乘以和除以两个d位数字。
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)
给定一个字符串,在排序的所有排列中找到它的排名 字典顺序。例如,“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