1-2 字典序问题
问题描述
在数据加密和数据压缩中常需要对特殊的字符串进行编码。给定的字母表A由26个小写字母组成。该字母表产生的升序字符串中字母从左到右出现的次序与字母在字母表中出现的次序相同,且每个字符最多出现1次。例如,a,b,ab,bc,xyz等字符串都是升序字符串。现在对字母表中产生的所有长度不超过6的升序字符串,计算它在字典中的编码。
| 1 | 2 | … | 26 | 27 | 28 | … |
| a | b | … | z | ab | ac | … |
分析1
以第i个字符打头的长度为k的升序字符串个数为f(i,k)f(i,k),长度为k的升序字符串总个数为g(k)g(k),则g(k)g(k)=∑26i=1∑i=126f(i,k)f(i,k)。易知:
f(i,1)f(i,1)=1
g(1)g(1)=∑26i=1∑i=126f(i,1)f(i,1)=26
f(i,2)f(i,2)=∑26j=i+1∑j=i+126f(j,1)f(j,1)=26-i
g(2)g(2)=∑26i=1∑i=126f(i,2)f(i,2)=∑26i=1∑i=126(26−i)(26−i)=325
…
f(i,k)f(i,k)=∑26j=i+1∑j=i+126f(j,k−1)f(j,k−1)
g(k)g(k)=∑26i=1∑i=126f(i,k)f(i,k)=∑26i=1∑i=126∑26j=i+1∑j=i+126f(j,k−1)f(j,k−1)
Java: version-1
import java.io.*;
public class Main {
public static void main(String[] args) {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String str = null;
int strLen;
while (true){
int value = 0;
System.out.println("Enter the string:");
try {
str = br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
if(str != null){
strLen = str.length();
//小于输入字符串长度的升序字符串总个数
for(int i=1; i<strLen; i++){
value += totalNumByLen(i);
}
char[] strToChar = str.toCharArray();
//check the input string
boolean invalidString = false;
for(int p=0; p+1<strLen; p++){
if(strToChar[p] >= strToChar[p+1]){
System.out.println("------------------");
System.out.println(str+" invalid!");
System.out.println("Please input a valid string!");
System.out.println("------------------");
invalidString = true;
continue;
}
}
if(invalidString){
continue;
}
int[] charToInt = new int[strLen];
for(int j=0; j<strLen; j++){
charToInt[j] = (int)strToChar[j]-96;
}
/*//以小于输入字符串的第一个字符打头长度为strLen的升序字符串个数
for(int l=1,r=charToInt[0]; l<r; l++){
value += numByLen(l,strLen);
}
//以输入字符串相邻两个字符之间的字符打头长度为strLen-k的升序字符串个数
for(int k=1; k<strLen; k++){
for(int l=charToInt[k-1]+1,r=charToInt[k]; l<r; l++){
value += numByLen(l,strLen-k);
}
}*/
//将上面两个函数合为一个
//以输入字符串相邻两个字符之间的字符打头长度为strLen-k的升序字符串个数
int initial =1;
for(int k=0; k<strLen; k++){
for(int l=initial,r=charToInt[k]; l<r; l++){
value += numByLen(l,strLen-k);
}
initial = charToInt[k]+1;
}
value++;//加上输入字符串本身占有的1
System.out.println(value);//此时value的值即为输入字符串在字典中的编码
System.out.println("------------------");
}
}
}
//以第i个字符打头的长度为k的升序字符串个数
private static int numByLen(int i, int k){
int tempNum = 0;
if(k == 1)
return 1;
else
for(int j=i+1; j<=26; j++){
tempNum += numByLen(j,k-1);
}
return tempNum;
}
//长度为k的升序字符串总个数
private static int totalNumByLen(int k){
int tempTotalNum = 0;
for(int i=1; i<=26; i++){
tempTotalNum += numByLen(i,k);
}
return tempTotalNum;
}
}
分析2
运用排列组合思想计算f(i,k)f(i,k)和g(k)g(k)
Java: version-2
import java.io.*;
public class Main {
public static void main(String[] args) {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String str = null;
int strLen;
while (true){
int value = 0;
System.out.println("Enter the string:");
try {
str = br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
if(str != null){
strLen = str.length();
//小于输入字符串长度的升序字符串总个数
for(int i=1; i<strLen; i++){
value += C(26, i);
}
char[] strToChar = str.toCharArray();
//check the input string
boolean invalidString = false;
for(int p=0; p+1<strLen; p++){
if(strToChar[p] >= strToChar[p+1]){
System.out.println("------------------");
System.out.println(str+" invalid!");
System.out.println("Please input a valid string!");
System.out.println("------------------");
invalidString = true;
continue;
}
}
if(invalidString){
continue;
}
int[] charToInt = new int[strLen];
for(int j=0; j<strLen; j++){
charToInt[j] = (int)strToChar[j]-96;
}
/*//以小于输入字符串的第一个字符打头长度为strLen的升序字符串个数
for(int l=1,r=charToInt[0]; l<r; l++){
value += C(26-l,strLen-1);
}
//以输入字符串相邻两个字符之间的字符打头长度为strLen-k的升序字符串个数
for(int k=1; k<strLen; k++){
for(int l=charToInt[k-1]+1,r=charToInt[k]; l<r; l++){
value += C(26-l,strLen-k-1);
}
}*/
//将上面两个函数合为一个
//以输入字符串相邻两个字符之间的字符打头长度为strLen-k的升序字符串个数
int initial = 1;
for(int k=0; k<strLen; k++){
for(int l=initial,r=charToInt[k]; l<r; l++){
value += C(26-l,strLen-k-1);
}
initial = charToInt[k]+1;
}
value++;//加上输入字符串本身占有的1
System.out.println(value);//此时value的值即为输入字符串在字典中的编码
System.out.println("------------------");
}
}
}
//计算组合数C(n,m)
private static long C(int n, int m) {
if( m > n )
return 0;
if( m > n/2 )
{
m = n - m;
}
return factorial(n, m)/factorial(m, m);
}
private static long factorial(int n, int m) {
long sum = 1;
while(m > 0 && n > 0) {
sum = sum * n--;
m--;
}
return sum;
}
}
Sample input & output
Enter the string:
a
1
------------------
Enter the string:
b
2
------------------
Enter the string:
z
26
------------------
Enter the string:
ab
27
------------------
Enter the string:
ac
28
------------------
Enter the string:
av
47
------------------
Enter the string:
bc
52
------------------
Enter the string:
xyz
2951
------------------
Enter the string:
aa
------------------
aa invalid!
Please input a valid string!
------------------
Enter the string:
ba
------------------
ba invalid!
Please input a valid string!
------------------
Enter the string:
dijk
9771
------------------
Enter the string:
Reference
王晓东《计算机算法设计与分析》(第3版)P6-7

本文详细探讨了字典序问题,特别是在数据加密和数据压缩中的应用。通过分析给出了长度不超过6的升序字符串在字典中的编码计算方法。文章提供了两种Java实现版本,分别基于递推关系和排列组合思想进行计算。
1648





