我正在尝试找到一种有效的算法来获取分区字符串的所有方法
例如对于给定的字符串'abcd'=>
'a''bcd'
'a''b''cd'
'a''b''c''d'
'ab''cd'
'ab''c''d'
'abc''d'
'a','bc','d
任何语言都会受到赞赏
提前致谢!
答案 0 :(得分:4)
问题分析
在每对相邻字符之间,您可以决定是否剪切。对于大小为n
的字符串,您可以剪切或不剪切n-1
个位置,即有两种可能性。因此,每个大小为2^(n-1)
的字符串都有n
个分区。
由于输出的大小(2^(n-1)
分区,每个分区由字符串+分隔符中的n
个字符组成),解决此问题的算法最多可以具有指数运行时间({{1 }→2^(n-1) * n
)。
<强>解决方案强>
我选择将分区表示为整数。 O(2^n)
中的每个位都决定是否在字符cutpoints
和i
之间切换。要遍历所有可能的分区,我们只需要遍历i+1
和0
之间的所有整数。
示例:对于长度为4的字符串,我们遍历2^(n-1) - 1
和0
或2^3 - 1
和0
之间的所有整数或二进制:7
和000
。
111
内存使用情况:
我认为我的解决方案在Python 3中使用# (python 2 or 3)
def all_partitions(string):
for cutpoints in range(1 << (len(string)-1)):
result = []
lastcut = 0
for i in range(len(string)-1):
if (1<<i) & cutpoints != 0:
result.append(string[lastcut:(i+1)])
lastcut = i+1
result.append(string[lastcut:])
yield result
for partition in all_partitions("abcd"):
print(partition)
内存。一次只生成一个分区,它已打印,不再被引用。如果保留所有结果,例如,这种情况会发生变化。将它们存储在列表中。
在Python 2中,将O(n)
替换为range
,否则所有可能的xrange
都将存储在列表中,因此需要指数量的内存。
JavaScript解决方案
cutpoints
使用NodeJS v4.4.3测试(免责声明:之前我没有使用过NodeJS。)
答案 1 :(得分:1)
这是一种利用内置迭代器最大限度缩短开发人员时间的解决方案。对于问题规模而言应该相当快,答案本身并不大。
字符串的分区与潜在分界点的子集之间存在一对一的对应关系。如果字符串的长度为n
,则可以在n-1
个位置剪切字符串。一种直接的方法是迭代这些子集,并且对于每个这样的子集,以这种方式对字符串进行切片。这是一种使用标准模块itertools
:
import itertools
def multiSlice(s,cutpoints):
k = len(cutpoints)
if k == 0:
return [s]
else:
multislices = [s[:cutpoints[0]]]
multislices.extend(s[cutpoints[i]:cutpoints[i+1]] for i in range(k-1))
multislices.append(s[cutpoints[k-1]:])
return multislices
def allPartitions(s):
n = len(s)
cuts = list(range(1,n))
for k in range(n):
for cutpoints in itertools.combinations(cuts,k):
yield multiSlice(s,cutpoints)
例如:
>>> parts = allPartitions('World')
>>> for p in parts: print(p)
['World']
['W', 'orld']
['Wo', 'rld']
['Wor', 'ld']
['Worl', 'd']
['W', 'o', 'rld']
['W', 'or', 'ld']
['W', 'orl', 'd']
['Wo', 'r', 'ld']
['Wo', 'rl', 'd']
['Wor', 'l', 'd']
['W', 'o', 'r', 'ld']
['W', 'o', 'rl', 'd']
['W', 'or', 'l', 'd']
['Wo', 'r', 'l', 'd']
['W', 'o', 'r', 'l', 'd']
请注意,此方法会生成['World']
作为'World'
的分区。这对应于使用一组空切割点进行切片。我认为这是一个功能而不是一个错误,因为分区的标准数学定义允许将一个分区分成一个部分。如果这对您的目的不利,那么修复很容易 - 只需迭代切割点的非空子集。就上述代码而言,此修复相当于向allPartitions
添加两个字符:replace
for k in range(n):
通过
for k in range(1,n):
答案 2 :(得分:0)
以下内容(未经测试且可能有缺陷的VB.NET示例)
Function FindAllGroups(s As String) As List(Of List(Of String))
Dim ret As New List(Of List(Of String))
Dim l As New List(Of String)
l.Add(s) 'the whole string unbroken
ret.Add(l) 'first option we return is the whole unbroken string by itself
If s.Length > 1 Then
Dim tmp = FindAllGroups(s.Substring(1)) 'find all the groups for the rest of the string after the first character
For Each l2 in tmp
l = l2.ToList 'Copy it
l.Insert(s.SubString(0,1),0)'insert the first character from this string by itself before this combination for the rest of the string
ret.Add(l)
Next
For Each l2 in tmp
l = l2.ToList 'Copy it
l(0)= s.SubString(0,1) & l(0) 'insert the first character from this string as part of the first element in the list
ret.Add(l)
Next
End If
Return ret
End Function
这基本上可以说我们可以把'abcd'分成
'a', 1st option for 'bcd' split
'a', 2nd option for 'bcd' split
...
+
1st option for 'bcd' split with the first element prepended with 'a'
2nd option for 'bcd' split with the first element prepended with 'a'
...
然后计算'bcd',我们只重复上面的过程,只有
'b', 1st option for 'cd' split
'b', 2nd option for 'cd' split
...
+
1st option for 'cd' split with the first element prepended with 'b'
2nd option for 'cd' split with the first element prepended with 'b'
...
等。反复递归。
但是,此代码在运行时不是特别有效。你可以做的一件事就是在一个函数之外添加一个Dictionary(Of String,List(Of List(Of String)),你可以存储结果的缓存,如果该项存在于那里你从那里返回,如果没有,计算它并添加它。列表也可能不是最有效的,而ToList函数可能不是克隆的最快方式。但是,我已经简化它以使它更容易理解并且还节省了我的工作时间!
答案 3 :(得分:0)
GeeksforGeeks为这个问题提供了一个解释良好的解决方案:
对于字符串abcd
,将有2 ^(n-1),即8个分区。
(a)(b)(c)(d)
(a)(b)(cd)
(a)(bc)(d)
(a)(bcd)
(ab)(c)(d)
(ab)(cd)
(abc)(d)
(abcd)
解决方案的关键在于recursion
打印所有排列
维护两个参数 - 到目前为止要处理的下一个字符的索引和输出字符串。我们从要处理的下一个字符的索引开始,将由未处理的字符串形成的子字符串追加到输出字符串并递归剩余的字符串,直到我们处理整个字符串。
// Java program to find all combinations of Non-
// overlapping substrings formed from given
// string
class GFG
{
// find all combinations of non-overlapping
// substrings formed by input string str
static void findCombinations(String str, int index,
String out)
{
if (index == str.length())
System.out.println(out);
for (int i = index; i < str.length(); i++)
// append substring formed by str[index,
// i] to output string
findCombinations(str, i + 1, out +
"(" + str.substring(index, i+1) + ")" );
}
// driver program
public static void main (String[] args)
{
// input string
String str = "abcd";
findCombinations(str, 0, "");
}
}
时间复杂度为O(2 ^ n)
这是指文章的链接:http://www.geeksforgeeks.org/print-ways-break-string-bracket-form/
答案 4 :(得分:0)
我只想为遇到这个问题的任何人发布一个简单的递归解决方案。可能不是最好的方法,但这对我来说更容易理解和实现。如果我错了,请纠正我。
def party(s:str, P:list, res:list) -> None :
"""Recursively generates all partitions of a given string"""
res.append(P+[s])
for i in range(1,len(s)):
party(s[i:],P+[s[:i]],res)
res = []
party("abcd",[],res)
print(res)
"""
[['abcd'], ['a', 'bcd'], ['a', 'b', 'cd'], ['a', 'b', 'c', 'd'],
['a', 'bc', 'd'], ['ab', 'cd'], ['ab', 'c', 'd'], ['abc', 'd']]
"""
它的工作原理如下: 给定一个字符串或一个子字符串,我们可以在每个字符分成两个部分之后进行拆分。 说:“ abc”可以分为[“ a”,“ bc”],[“ ab”,“ c”]
我们将第一部分保存在中间分区P
中,然后
另一半递归调用party
。
由于两个半部组成一个完整的分区,因此将其保存到res
中。
示例:
最初:s =“ abc”是有效分区,将其保存到res。
recr调用:s =“ bc”,P = [“ a”],所以P + [s] = [“ a”,“ bc”]也有效,将其保存到res
。 / p>
继续拆分“ bc”。 P = [“ a”,“ b”],s =“ c”,因此P + [s]也有效。等等。
recr调用3:s =“ c”,P = [“ ab”],所以P + [s] = [“ ab”,“ c”]也有效,将其保存到res
< / p>
工作:
tests = ["abc","abcd","a"]
for t in tests:
res = []
party(t,[],res)
print(f'{t} -> {res} \n')
"""Output
abc -> [['abc'], ['a', 'bc'], ['a', 'b', 'c'], ['ab', 'c']]
abcd -> [['abcd'], ['a', 'bcd'], ['a', 'b', 'cd'], ['a', 'b', 'c', 'd'],
['a', 'bc', 'd'], ['ab', 'cd'], ['ab', 'c', 'd'], ['abc', 'd']]
a -> [['a']]
"""