1 题目:
输入参数:一个字符串,这个字符串只包含字母A-Z或者a-z
返回值:返回输入参数的所有顺序子串,不同的子串之间用逗号分隔。
顺序子串的定义是:只由输入参数中出现的字母,按照其在输入参数中出现的顺序组成的字符串。
例如:输入参数为 “abc”,返回值应该为: “abc, ab, ac, bc, a, b, c”。
该函数的Specification为:
/**
* @param word consisting only of letters A-Z or a-z
* @return all subsequences of word, separated by commas,
* where a subsequence is a string of letters found in word
* in the same order that they appear in word.
*/
public static String subsequences(String word)
2 递归解法
2.1 分析:
假设输入参数为:
a1a2a3...an
, 其中
a1,a2,...,an
代表输入参数中的每一个字母。
设
S(n)
表示一个集合, 其元素是:
a1
到
an
共 n 个字母的所有顺序子串,
S(n−1)
表示一个集合,其元素是:
a2
到
an
共 n-1 个字母的所有顺序子串,以此类推,如下图所示,直到得到
S(1)
的定义。
S(0)
定义为空字符串, 为递归算法的 Base Case, 。
定义函数 concatenate(ai,S(n)) : 表示将 ai 与集合 S(n) 中的每个元素进行连接操作。
于是有如下递归方程:
可以根据下面的例子理解上述方法:
设输入参数为: a1a2a3="abc" , "" 代表空字符串
S(0)=""S(1)=S(0)+concatenate(a3,S(0))=c,""
S(1)=c,""S(2)=S(1)+concatenate(a2,S(1))=bc,b,c,""
S(2)=bc,b,c,""S(3)=S(2)+concatenate(a1,S(2)),即
S(3)=abc,ab,ac,bc,a,b,c,""
时间复杂度:
由于输入参数所有的子序列都需要输出,将输出参数视为大小为n的集合,子序列为其子集和。大小为n的集合所有子集的个数为 Pow(n)=2n , 所以时间复杂度为 O(2n) 。
2.2 设计递归方法
2.2.1 根据题目的约定(Specification)设计递归方法
/**
* @param word consisting only of letters A-Z or a-z
* @return all subsequences of word, separated by commas,
* where a subsequence is a string of letters found in word
* in the same order that they appear in word.
*/
public static String subsequences(String word) {
if (word.isEmpty()) {
return ""; // base case
} else {
char firstLetter = word.charAt(0);
String restOfWord = word.substring(1);
String subsequencesOfRest = subsequences(restOfWord);
String result = "";
for (String subsequence : subsequencesOfRest.split(",", -1)) {
result += "," + subsequence;
result += "," + firstLetter + subsequence;
}
result = result.substring(1); // remove extra leading comma
return result;
}
}
2.2.2 设计Helper方法使递归更简洁
根据递归方程设计递归方法时候,可以设计helper方法(如下),使递归方程的代码实现更加简洁。
将前i个字母的一个顺序子串作为参数传入递归方法中, 实现同样的功能,使代码量减少,易懂。
/**
* Return all subsequences of word (as defined above) separated by commas,
* with partialSubsequence prepended to each one.
*/
private static String subsequencesAfter(String partialSubsequence, String word) {
if (word.isEmpty()) {
// base case
return partialSubsequence;
} else {
// recursive step
return subsequencesAfter(partialSubsequence, word.substring(1))
+ ","
+ subsequencesAfter(partialSubsequence + word.charAt(0), word.substring(1));
}
}
Reference
[1] 6.005 — Software Construction on MIT OpenCourseWare | OCW 6.005 Homepage at https://ocw.mit.edu/ans7870/6/6.005/s16/