如何处理字谜搜索期间字符串排列的时间复杂度?
Posted
技术标签:
【中文标题】如何处理字谜搜索期间字符串排列的时间复杂度?【英文标题】:How to handle the time complexity for permutation of strings during anagrams search? 【发布时间】:2020-07-07 02:35:21 【问题描述】:我有一个程序可以计算两个字符串是否是字谜。 它适用于长度小于 10 的字符串输入。 当我输入两个长度相等且长度超过 10 次的字符串时,程序运行并没有产生答案。
我的概念是,如果两个字符串是字谜,一个字符串必须是另一个字符串的排列。
这个程序从一个字符串生成所有排列,然后它检查另一个字符串是否有任何匹配的排列。在这种情况下,我想忽略案例。 如果没有找到匹配的字符串或比较的字符串长度不相等,则返回false,否则返回true。
public class Anagrams
static ArrayList<String> str = new ArrayList<>();
static boolean isAnagram(String a, String b)
// there is no need for checking these two
// strings because their length doesn't match
if (a.length() != b.length())
return false;
Anagrams.permute(a, 0, a.length() - 1);
for (String string : Anagrams.str)
if (string.equalsIgnoreCase(b))
// returns true if there is a matching string
// for b in the permuted string list of a
return true;
// returns false if there is no matching string
// for b in the permuted string list of a
return false;
private static void permute(String str, int l, int r)
if (l == r)
// adds the permuted strings to the ArrayList
Anagrams.str.add(str);
else
for (int i = l; i <= r; i++)
str = Anagrams.swap(str, l, i);
Anagrams.permute(str, l + 1, r);
str = Anagrams.swap(str, l, i);
public static String swap(String a, int i, int j)
char temp;
char[] charArray = a.toCharArray();
temp = charArray[i];
charArray[i] = charArray[j];
charArray[j] = temp;
return String.valueOf(charArray);
1.我想知道为什么这个程序不能处理更大的字符串
2.我想知道如何解决这个问题
你能猜到吗?
【问题讨论】:
如果你有一个由 11 个阶乘排列组成的字符串,它是 39916800 大数,对吗?它正在处理它,你最终会在控制台上弹出结果 1) 您的解决方案的时间和空间复杂度很大。因为有n!排列,其中n
是字符串的长度。 2)你可以检查两个字符串是否是线性时间和恒定空间复杂度的字谜。
@AbhinavChauhan 是的,我知道我总是像这些大递归一样卡住,有没有一种机制可以消除这样的问题
@jpact 那么有没有一种机制可以处理这种情况?
@NemindaPrabhashwara 请参阅下面的解决方案
【参考方案1】:
要解决这个问题并检查两个字符串是否是字谜,您实际上不需要生成源字符串的每个排列,然后将其与第二个排列匹配。您可以做的是计算第一个字符串中每个字符的频率,然后验证相同的频率是否适用于第二个字符串。
上面的解决方案需要对每个字符串进行一次传递,因此 Θ(n) 时间复杂度。此外,您需要辅助存储来计算 Θ(1) 空间复杂度的字符。这些是渐近紧密的界限。
【讨论】:
用于计数字符的辅助存储是 O(n) 空间复杂度,而不是 O(1) 空间复杂度。 @JimMischel 不,您的字符集中的每个字符都需要一个存储位置(通常被认为是有界的)。例如,如果您的字符串是字节字符串,那么无论字符串大小如何,都只需要 256 个计数器。 @PaulHankin 好的,所以你需要 O(|alphabet|),正如你所说,它是一个常数。【参考方案2】:您正在以非常昂贵的方式进行操作,并且这里的时间复杂度是指数级的,因为您正在使用需要阶乘的排列,并且阶乘增长非常快,因为您正在做排列,当输入大于 10。
11 factorial = 39916800
12 factorial = 479001600
13 factorial = 6227020800
等等……
所以不要以为你没有得到大数字的输出,你最终会得到它
如果你使用 20-30 阶乘,我想我需要数年时间才能产生任何输出,如果你使用循环,使用递归你会溢出堆栈。
事实: 50 阶乘是一个比地球上沙粒数量还要大的数字,当计算机必须处理这么大的数字时,它们就会投降。
这就是为什么他们让你在密码中包含特殊字符,以使排列的数量太大,以至于如果他们尝试每一个排列,计算机将无法破解它多年,而加密也取决于计算机的弱点。
所以你不必也不应该这样做来解决它(因为计算机不是很擅长),这是一个矫枉过正
你为什么不从一个字符串中取出每个字符并将其与另一个字符串的每个字符匹配,在最坏的情况下它会是二次的。
如果你对两个字符串都进行排序,那么你可以说
string1.equals(string2)
true
表示字谜
false
表示不是字谜
除了排序所用的时间外,它会花费线性时间。
【讨论】:
@NemindaPrabhashwara 检查编辑,您可以将其标记为已回答,一切顺利 但是还有一个问题与这个无关,有很多问题需要递归,有时会导致堆栈溢出。难道没有像动态规划这样的机制来克服这个问题吗? @NemindaPrabhashwara 没问题需要递归,如果你可以用递归来做,那么你也可以迭代地做,但递归有时会提供一个简单而简短的解决方案,但如果你知道你要去进入深度递归然后不要使用它,递归也比迭代解决方案效率低,因为方法调用是额外的开销。 @NemindaPrabhashwara 如果你必须使用深度递归,那么你也可以增加堆栈大小,检查这里***.com/questions/3700459/…【参考方案3】:你可以先从这些字符串中获取字符数组,然后sort
它们,然后比较两个排序后的数组。此方法适用于常规字符和代理对。
public static void main(String[] args)
System.out.println(isAnagram("ABCD", "DCBA")); // true
System.out.println(isAnagram("????", "????")); // true
static boolean isAnagram(String a, String b)
// invalid incoming data
if (a == null || b == null
|| a.length() != b.length())
return false;
char[] aArr = a.toCharArray();
char[] bArr = b.toCharArray();
Arrays.sort(aArr);
Arrays.sort(bArr);
return Arrays.equals(aArr, bArr);
另见:Check if one array is a subset of the other array - special case
【讨论】:
以上是关于如何处理字谜搜索期间字符串排列的时间复杂度?的主要内容,如果未能解决你的问题,请参考以下文章
如何处理 DATEDIFF(MINUTE, '00:00', '24:20') 类似的场景?