给定两个有限的字符串序列
A
和B
,每个字符串长度n
, 例如:A1: "kk", A2: "ka", A3: "kkk", A4: "a" B1: "ka", B2: "kakk", B3: "ak", B4: "k"
给出有限的索引序列,使其浓度为A 和B给出相同的字符串。允许重复。
在此示例中,我找不到解决方案,但是例如,如果列表(1,2,2,4)
是解决方案,那么A1 + A2 + A2 + A4 = B1 + B2 + B2 + B4
。在这个例子中只有两个字符,但它已经非常困难。实际上,用一个角色找到最短的解决方案甚至是微不足道的!
我试着想一想......例如,字符串长度的总和必须相等,而对于第一个和最后一个字符串,我们需要相应的字符。但没有别的。我想对于一些字符串来说,根本不可能。任何人都可以想到一个好的算法?
编辑:显然,这是Post Correspondence Problem
没有算法可以决定这样的实例是否有解决方案。如果有,停止问题可以解决。肮脏的把戏......
答案 0 :(得分:2)
非常棘手的问题,但我会试一试。这更像是一种意识流而不是一种答案,提前道歉。
如果我理解正确的话,你会得到2个大小相等的字符串序列,A和B,从1..n索引,比方说。然后,您必须找到一系列索引,使得字符串A(1)... A(m)的串联等于字符串B(1)的串联.B(m)其中m是索引序列的长度
我要注意的第一件事是可以有无数的解决方案。例如,给定:
A {“x”,“xx”}
B {“xx”,“x”}
可能的解决方案是:
{1,2} {2,1} {1,2,1,2}
{1,2,2,1} {2,1,1,2} {2,1,2,1} {1,2,1,2,1,2} ...
那你怎么知道什么时候停下来?只要你有一个解决方案?只要其中一个解决方案是另一个解决方案的超集?
你可以开始的一个地方是从两个集合中获取所有最小公共长度的字符串(在上面的例子中,你将从两者中取“x”,并搜索共享一个公共索引的2个相等的字符串。然后你可以为下一个大小的字符串重复这个。例如,如果第一个集合有3个长度分别为1,2和3的字符串,而第二个集合分别有长度为1,3和3的字符串,你可以采取长度为3的字符串。在没有更多字符串之前,你会这样做。如果找到任何字符串,那么你就可以找到解决问题的方法。
当你必须开始组合几个字符串时,它会变得更加困难,如上例所示。天真的暴力方法是开始置换两个集合中的所有字符串,当连接时,会产生相同长度的字符串,然后比较它们。所以在下面的例子中:
A {“ga”,“bag”,“ac”,“a”} B {“ba”,“g”,“ag”,“gac”}
您将从长度为2的序列开始:
A {“ga”,“ac”},B {“ba”,“ag”}(索引1,3)
A {“bag”,“a”},B {“g”,“gac”}(指数2,4)
比较这些给出了“gaac”vs“baag”和“baga”vs“ggac”,两者都不相同,所以那里没有解决方案。接下来,我们将选择长度为3的序列:
A {“ga”,“bag”,“a”},B {“ba”,“g”,“gac”}(索引1,2,4)
A {“bag”,“ac”,“a”},B {“g”,“ag”,“gac”}(指数2,3,4)
同样,没有解决方案,所以我们最终得到大小为4的序列,其中我们没有解决方案。
现在它变得更加棘手,因为我们必须开始考虑重复一些指数,现在我的大脑正在融化。
我在寻找字符串中常见的子序列可能会有所帮助,然后使用未匹配的字符串中的其余部分。但我不太清楚如何。
答案 1 :(得分:2)
一种非常简单的方法就是使用像广度优先搜索这样的东西。这也具有以下优点:找到的第一个解决方案具有最小尺寸。
答案 2 :(得分:0)
这是关于强力搜索的建议。首先生成与列表长度有关的数字序列:
[0,0,..] [1,0,..] [2,0,..] [3,0,..] [0,1,..] ...
数字序列长度决定了找到的任何解决方案中的字符串数量。 然后使用数字作为索引到字符串列表中生成A和B字符串:
public class FitSequence
{
private readonly string[] a;
private readonly string[] b;
public FitSequence(string[] a, string[] b)
{
this.a = a;
this.b = b;
}
private static string BuildString(string[] source, int[] indexes)
{
var s = new StringBuilder();
for (int i = 0; i < indexes.Length; ++i)
{
s.Append(source[indexes[i]]);
}
return s.ToString();
}
public IEnumerable<int[]> GetSequences(int length)
{
foreach (var numberSequence in new NumberSequence(length).GetNumbers(a.Length - 1))
{
string a1 = BuildString(a, numberSequence);
string b1 = BuildString(b, numberSequence);
if (a1 == b1)
yield return numberSequence;
}
}
}
该算法假定A和B的长度相等。 我用
测试了你的例子 static void Main(string[] args)
{
var a = new[] {"kk", "ka", "kkk", "a"};
var b = new[] {"ka", "kakk", "ak", "k"};
for (int i = 0; i < 100; ++i)
foreach (var sequence in new FitSequence(a, b).GetSequences(i))
{
foreach (int x in sequence)
Console.Write("{0} ", x);
Console.WriteLine();
}
}
但找不到任何解决方案,尽管它似乎适用于简单的测试。
答案 3 :(得分:0)
目前尚不清楚您正在寻找的“解决方案”是最长的解决方案?最短的?所有解决方案?
既然你允许重复,那么对于某些输入会有无数的解决方案,所以我将继续努力:
作为伪代码编写,但其方式与f#序列表达式非常相似
// assumed true/false functions
let Eq aList bList =
// eg Eq "ab"::"c" "a" :: "bc" -> true
// Eq {} {} is _false_
let EitherStartsWith aList bList =
// eg "ab"::"c" "a" :: "b" -> true
// eg "a" "ab" -> true
// {} {} is _true_
let rec FindMatches A B aList bList level
= seq {
if level > 0
if Eq aList bList
yield aList
else if EitherStartsWith aList bList
Seq.zip3 A B seq {1..}
|> Seq.iter (func (a,b,i) ->
yield! FindMatches A B aList::(a,i) bList::(b,i) level - 1) }
let solution (A:seq<string>) (B:seq<string>) length =
FindMatches A B {} {} length
减少问题的一些微不足道的限制:
基于此,我们可以快速消除许多没有解决方案的输入
let solution (A:seq<string>) (B:seq<string>) length =
let starts = {}
let ends = {}
Seq.zip3 A B seq {1..}
|> Seq.iter(fun (a,b,i) ->
if (a.StartsWith(b) or b.StartsWith(a))
start = starts :: (a,b,i)
if (a.EndsWith(b) or b.EndsWith(a))
ends = ends :: (a,b,i))
if List.is_empty starts || List.is_empty ends
Seq.empty // no solution
else
Seq.map (fun (a,b,i) ->
FindMatches A B {} :: (a,i) {} :: (b,i) length - 1)
starts
|> Seq.concat