我想用字母a,b和c生成所有可能的长度为n的字符串。我收到此错误java.lang.OutOfMemoryError
:Java堆空间。我的堆大小是512米。你能为我提出替代方案吗?
答案 0 :(得分:5)
您当前正在计算的字符串数量为
3^30 = 205 891 132 094 649
这是非常多的......
知道每个String包含三个字节:
3^30 * 3 = 617 673 396 283 947
加上二维数组的32位或64位指针。
3^30 * (3 + 4) = 1 441 237 924 662 540 // 32-bit Java VM
3^30 * (3 + 8) = 2 264 802 453 041 140 // 64-bit Java VM
哪个是
2 109 261 GB = 2059 TB // 64-bit JVM
我想这就是问题所在。
限制为500 MB,您可以解决这个等式:
3^x * (3 + 8) = 524 288 000
3^x = 47662545
x = log(47662545) / log(3)
x = 7 / 0.477121254719662
x = 14.67
所以,如果我什么也没有忘记,那么你的测试应该适用于n <= 14
。当然,除非删除此代码,否则它将无效:
List<String> result = new ArrayList<String>();
for (char[] permutation : table) {
result.add(new String(permutation));
}
此代码复制了所有数据!这意味着您需要两倍的内存量。尝试立即打印。
答案 1 :(得分:1)
我在你的评论中指出你有一个不同的问题。你想找到长度为30的所有字符串,a-z不包含a-c。这是长度为30的所有字符串的计数,即d-z。计数是(26-3)^ 30。
System.out.printf("%,d%n", BigInteger.valueOf(26-3).pow(30));
打印
71,094,348,791,151,363,024,389,554,286,420,996,798,449
您可以将每个可能的String编码为数字,而不是记住每个字符串。在您的情况下,您可以使用long
。
public static void main(String... args) {
String letters = "abc";
int len = 30;
long combinations = (long) Math.pow(letters.length(), len);
System.out.printf("There are %,d strings%n", combinations);
for (long i = 0; i < 10; i++)
System.out.println(fromLong(i, letters, len));
System.out.println("... some numbers skipped ...");
for (long i = combinations-10; i < combinations; i++)
System.out.println(fromLong(i, letters, len));
}
public static String fromLong(long n, String letters, int len) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < len; i++) {
sb.append(letters.charAt((int) (n % letters.length())));
n /= letters.length();
}
return sb.reverse().toString();
}
打印
There are 205,891,132,094,649 strings
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaab
aaaaaaaaaaaaaaaaaaaaaaaaaaaaac
aaaaaaaaaaaaaaaaaaaaaaaaaaaaba
aaaaaaaaaaaaaaaaaaaaaaaaaaaabb
aaaaaaaaaaaaaaaaaaaaaaaaaaaabc
aaaaaaaaaaaaaaaaaaaaaaaaaaaaca
aaaaaaaaaaaaaaaaaaaaaaaaaaaacb
aaaaaaaaaaaaaaaaaaaaaaaaaaaacc
aaaaaaaaaaaaaaaaaaaaaaaaaaabaa
... some numbers skipped ...
cccccccccccccccccccccccccccbcc
ccccccccccccccccccccccccccccaa
ccccccccccccccccccccccccccccab
ccccccccccccccccccccccccccccac
ccccccccccccccccccccccccccccba
ccccccccccccccccccccccccccccbb
ccccccccccccccccccccccccccccbc
ccccccccccccccccccccccccccccca
cccccccccccccccccccccccccccccb
cccccccccccccccccccccccccccccc
这可以打印从0到3 ^ 30-1的每个可能的字符串。您不需要存储所有编码值,因为您知道所有可能的值都在连续范围内。
答案 2 :(得分:1)
计算不包含子串“ABC”的字符串可以通过动态编程来解决。
首先,让我们将长度为n的所有字符串的编号命名为S(n)
。请注意,S(n)
很容易计算。 S(n) = pow(size_of_the_alphabet,n)
让我们将包含长度为n的“ABC”的所有字符串的编号命名为A(n)
,并将第k次位置首次出现“ABC”的所有字符串的编号命名为A(n,k)
现在请注意:
A(n,k) = (S(k-1) - A(k-1)) * S(n - k - 3)
(因为k是发生“ABC”的第一个位置,这些字符串中的每一个在此位置之前都有一个没有“ABC”的子字符串,在第一个“ABC”之后有一个子字符串。)
请注意A(n) = sum A(n,k) for k in [0..n-3]
现在我们可以计算A(n)
从0开始计算A(n)
的每个值。
基本案例很简单,如A(n) = 0 for n = 0,1,2
A(3) = (S(0) - A(0))*S(0) = 1
(即“ABC”)
等。
获得A(n)
后,您可以使用公式S(n) - A(n)
获取您要查找的号码。
Pseudojava伪代码:
public class Counter {
public int count(int aSize, int n) {
long[] a = new long[n+1]; // n + 1 elements since a[i] contains # of strings containing "ABC"
a[0] = 0;
a[1] = 0;
a[2] = 0;
for (int i = 3; i <= n; ++i){
long sum = 0;
for (int k = 0; k <= i-3; ++k) {
sum += (pow(aSize, k) - a[k]) * pow(aSize, i - k - 3);
}
a[i] = sum;
}
return a[n];
}
public static void main(String... args) {
int aSize = 3; //size of the alphabet
int n = 30; // length of the strings
//final result
long result = pow(aSize, n) - count(aSize, n);
}
}
运行时间为O(n^2)
,假设pow为O(1)。如果不是那么你可以节省一些预先计算S(i)的时间。
空间要求为O(n)
。
请注意,这会计算长度为== n的所有字符串的数量。如果你想要长度&lt; = n那么修改是显而易见的(你只需加总a中的所有元素。)
答案 3 :(得分:0)
您是在尝试计算字符串还是计算字符串?
编辑:我从后面的评论中看到你知道你在谈论多少个字符串:
如果你只是计算,这是一个标准的排列封闭形式:26^n
= 26提升到n次幂(假设你只使用从'a'到'z'的小写字母)。 /德尔>
如果您真的想要枚举每个字符串,我强烈建议您确保不保留对每个String
的引用。如果您最终对这些字符串中的每个字符串悬挂引用,那么在大约六个字符后,您将会耗尽内存。