两个字符串的交错

Posted

技术标签:

【中文标题】两个字符串的交错【英文标题】:Interleaving of two strings 【发布时间】:2011-10-11 21:48:03 【问题描述】:

我有两个字符串 str1str2。 是否有任何算法可用于使用递归打印出两个字符串的所有交错?

更新:

public class Interleave 


    private String resultString[] = new String[10];
    private String[] interStr(String str1, String str2)
    int n = ((Factorial.factorial(str1.length() + str2.length())) / (Factorial.factorial(str1.length()) * Factorial.factorial(str2.length())));
    //n is number of interleavings based on (str1.length()+str2.length())! / (str1.length()! * str2.length()!)
    if(str1.length() == 0)
        resultString[0] = str2;
        return resultString;
    

    if(str2.length() == 0)
        resultString[0] = str1;
        return resultString;
    

    else
        for(int i = 0; i < n; i++)
            resultString[i]= str1.substring(0, 1) + interStr(str1.substring(1), str2.substring(1));

        
    
    return resultString;


    public static void main(String[] args) 
    Interleave obj = new Interleave();
    obj.interStr("12", "abc");
    for(int i = 0; i < obj.resultString.length; i ++)
        System.out.println(obj.resultString[i]);
    




【问题讨论】:

@Mitch Wheat:我尝试了不同的东西,但与其尝试重新发明***,为什么不使用更有意义的东西? @Andrew Thompson:请问为什么我的帖子被编辑了? 因为人们希望看到您首先尝试自己解决问题,而不是希望有人为您解决问题。更不用说它看起来很像家庭作业。 @Mitch Wheat:我不希望任何人为我解决问题。 如何定义两个字符串的“交错”? 【参考方案1】:

该问题只是询问该问题是否存在递归算法,答案是肯定的。要找到它,请查找基本情况,然后查找“步骤”。

基本情况是两个字符串之一为空:

interleave(s1, "") = s1

interleave("", s2) = s2

注意参数的顺序并不重要,因为

interleave("ab", "12") = “ab12”、“a1b2”、“1ab2”、“a12b”、“1a2b”、“12ab” = interleave("12", "ab")

因此,由于顺序无关紧要,我们将考虑递归第一个字符串的长度。

好的,让我们看看一个案例如何导致下一个案例。我将仅使用一个具体的示例,您可以将其推广到实际代码中。

interleave("", "abc") = "abc" interleave("1", "abc") = “1abc”、“a1bc”、“ab1c”、“abc1” interleave("12", "abc") = “12abc”、“1a2bc”、“1ab2c”、“1abc2”、“a12bc”、“a1b2c”、“a1bc2”、“ab12c”、“ab1c2”“abc12”

所以每次我们向第一个字符串添加一个字符时,我们都会通过将新字符添加到旧结果集中所有可能的位置来形成新的结果集。让我们看看我们是如何从第二个结果形成上面的第三个结果的。当我们添加“2”时,第二个结果中的每个元素是如何变成第三个结果中的元素的?

"1abc" => "12abc", "1a2bc", "1ab2c", "1abc2" "a1bc" => "a12bc", "a1b2c", "a1bc2" "ab1c" => "ab12c", "ab1c2" "abc1" => "abc12"

现在这样看:

"1abc" => 1 w | w = interleave("2", "abc") "a1bc" => a1 w | w = interleave("2", "bc") "ab1c" => ab1 w | w = interleave("2", "c") "abc1" => abc1 w | w = interleave("2", "")

虽然一两个例子一般不能证明一个规则,但在这种情况下,您应该能够推断出规则是什么。您将有一个循环,其中包含递归调用。

这实际上用纯函数式编程更有趣,但你用 Java 标记了这个问题。

希望这对您来说是一个开始。如果您进一步卡住,您可以在网络上搜索“交错字符串”或“交错列表”。有一些解决方案。

编辑

好吧,我只是写了愚蠢的东西!用脚本语言编写这些东西很有趣,所以我想看看它在 Java 中的样子会很棒。没有我想象的那么糟糕!就是这样,打包成一个完整的 Java 应用程序。

import java.util.ArrayList;
import java.util.List;

public class Interleaver 

    /**
     * Returns a list containing all possible interleavings of two strings.
     * The order of the characters within the strings is preserved.
     */
    public static List<String> interleave(String s, String t) 
        List<String> result = new ArrayList<String>();
        if (t.isEmpty()) 
            result.add(s);
         else if (s.isEmpty()) 
            result.add(t);
         else 
            for (int i = 0; i <= s.length(); i++) 
                char c = t.charAt(0);
                String left = s.substring(0, i);
                String right = s.substring(i);
                for (String u : interleave(right, t.substring(1))) 
                    result.add(left + c + u);
                
            
        
        return result;
    

    /**
     * Prints some example interleavings to stdout.
     */
    public static void main(String[] args) 
        System.out.println(interleave("", ""));
        System.out.println(interleave("a", ""));
        System.out.println(interleave("", "1"));
        System.out.println(interleave("a", "1"));
        System.out.println(interleave("ab", "1"));
        System.out.println(interleave("ab", "12"));
        System.out.println(interleave("abc", "12"));
        System.out.println(interleave("ab", "1234"));
    

【讨论】:

感谢您的精彩解释。我试试看。 提示:符号x w | w = f(y) 表示通过将x 连接到调用f(y) 返回的每个字符串而形成的所有字符串的集合。它将变成一个for 循环。希望对您有所帮助。 @Nath 很难准确找出问题所在,但我确实看到您使用的是数组(并将数组固定为 10,这不是通用解决方案)而不是列表,因为您说你还没有学过。这不是使用数组的大问题,所以我建议你硬着头皮学习列表。为此,我继续为您编写了一个完整的 Java 应用程序。它应该按原样运行。 嗯,是的,在 for-i 循环中多次调用,并且随着我们深入递归级别多次调用。我只是说它在 for-u 循环中没有被多次调用。如果你只看 for (String u : interleave(...) 的 for 循环,你只调用 interleave 一次,然后迭代。与for (i=0; f(x); i++) 这样的经典for 语句形成对比,其中f(x) 在该循环中每次都被调用。希望能回答你的问题! java.sun.com/docs/books/jls/third_edition/html/…:在for(e1;e2;e3) 中,表达式e2 每次循环都会被计算。在for (type x: a)a 是一个数组表达式,表达式a 只计算一次。【参考方案2】:

如果我正确解释了您的问题 - 您想要两个字符串中所有字符的所有排列,那么以下代码将有所帮助。您将需要编写自己的交换函数,并以某种方式获取两个字符串中所有字符的数组。 该算法将从数组中的第 i 个元素到第 n 个元素进行置换。它是用 C++ 编写的,我会引用算法的来源,但我不记得了。

void getPermutationsR(char characters[], int n, int i) 

    if (i == n)
    
        //Output the current permutation
     
    else
    
        for (int j=i; j<n; j++) 
        
            swap (characters, i, j);
            getPermutationsR(characters, n, i+1);
            swap (characters, i, j);
        
    

【讨论】:

这不是 OP 要求的。【参考方案3】:

你现在拥有的是一个好的开始。问题是它只返回一个字符串,而不是这些字符串的列表。

更改您的函数以返回字符串列表,然后考虑如何组合多个列表以生成所需的所有输出。

【讨论】:

是的,但是您必须先计算正确的长度。对于列表,您可以简单地逐个添加元素。 好的。我明白了。我不知道为什么我在添加评论时必须输入至少 15 个字符。 他们想避免像“OK。我明白了”这样的琐碎的 cmets:-p 我会尝试使用您的建议和 Ray Toal 的算法。【参考方案4】:

这是一个使用递归方法的解决方案,也很容易理解

public class Interleave 

public static List<String> interleave(String first, String second)
    if(first.length() == 0)
        List<String> list = new ArrayList<String>();
        list.add(second);
        return list;
    
    else if(second.length() == 0)
        List<String> list = new ArrayList<String>();
        list.add(first);
        return list;
    
    else
        char c1 = first.charAt(0);
        List<String> listA =  multiply(c1,interleave(first.substring(1),second));
        char c2 = second.charAt(0);
        List<String> listB =  multiply(c2,interleave(first,second.substring(1)));
        listA.addAll(listB);
        return listA;
    



public static List<String> multiply(char c,List<String> list)
        List<String> result = new ArrayList<String>();
        for(String str : list)
            String res = Character.toString(c) + str;
            result.add(res);
        
    return result;


public static void main(String[] args)
    System.out.println(interleave("ab", "1234"));
    System.out.println(interleave("a", "b"));
    System.out.println(interleave("ab", "cd"));


 

【讨论】:

【参考方案5】:

以下是解决此问题的更好且更易于理解的解决方案:

public class Interleaver 

    /**
     * Returns a list containing all possible interleavings of two strings.
     * The order of the characters within the strings is preserved.
     */
    public static String s1 = "abc";
    public static String s2 = "12";

    public static void interleave(int i, int j, String s) 

        if (i == s1.length() && j == s2.length()) 
            System.out.println("" + s);
        

        if (i != s1.length()) 
            interleave(i + 1, j, s + s1.charAt(i));
        

        if (j != s2.length()) 
            interleave(i, j + 1, s + s2.charAt(j));
        

    //Method ends here

    /**
     * Prints some example interleavings to stdout.
     */
    public static void main(String[] args) 

        interleave(0, 0, "");
    //Method ends here
//Class ends here

程序仅使用简单的递归调用来找到解决方案。

【讨论】:

【参考方案6】:

这是另一个递归解决方案:

public class Interleaving2 
    public static void main(String[] args) 
        String x = "ab";
        String y = "CD";
        int m = x.length();
        int n = y.length();
        char[] result = new char[m + n + 1];

        interleave(x, y, result, m, n, 0);
    

    public static void interleave(String x, String y, char[] result, int m, int n, int i) 
        if (m == 0 && n == 0) 
            System.out.println(String.valueOf(result));
        

        if (m != 0) 
            result[i] = x.charAt(0);
            interleave(x.substring(1), y, result, m - 1, n, i + 1);
        

        if (n != 0) 
            result[i] = y.charAt(0);
            interleave(x, y.substring(1), result, m, n - 1, i + 1);
        
     

【讨论】:

以上是关于两个字符串的交错的主要内容,如果未能解决你的问题,请参考以下文章

如何交错或创建两个字符串的唯一排列(无递归)

java 交错的两个字符串

Leetcode No.97 交错字符串

交错01串

[leetcode] 97. 交错字符串

[编程题] 交错01串 网易2018