【问题标题】:Recursively generate ordered substrings from an ordered sequence of chars?从有序的字符序列递归生成有序子字符串?
【发布时间】:2010-10-28 03:11:27
【问题描述】:

得到答案后编辑

这里有一些很好的答案。我喜欢 Josh 的,因为它非常聪明并且使用 C++。但是,我决定接受 Dave 的回答,因为它简单且具有递归性。我对它们都进行了测试,它们都产生了相同的正确结果(尽管顺序不同)。再次感谢大家。


假设我有一个字符串 s 字符 s[0]:s[N] 并且每个字符 s[i]

aaacdddghzz

我想生成子字符串的所有组合,同时保持字符之间的相同关系。

所以例如我会得到

a
aa
aaa
ad
aad
aaad
add
aadd
aaadd
addd
aaddd
aaaddd
d
dd
ddd
.
.
.
ac
aac
.
.
.
acdddghzz
aacdddghzz
aaacdddghzz

但不是

ca
hdz
...etc

现在我知道如何计算出有多少种组合了。您创建字符串中字母频率的直方图。所以在上面的例子中,那将是

对于字符串 aaacdddghzz

a=3
d=3
c=1
g=1
h=1
z=2

公式为(a+1)(c+1)(d+1)(g+1)(h+1)(z+1) = 4*4*2*2*2*3 = 384。有 384 个子串保持 s[i]

所以问题是如何递归地生成这 384 个子字符串?实际上,迭代方法同样好,也许更好,因为具有许多唯一字符的大字符串可能会导致堆栈溢出。这听起来像是家庭作业,但事实并非如此。我只是想出这样的算法没用。我使用 C++,但伪代码就可以了。

【问题讨论】:

  • 澄清一下,您是否认为从不同位置获取的相同字母序列是相同的子字符串?即 [0..1] == "aa" 与 [1..2] == "aa" 相同吗?
  • 此外,我注意到您的结果中有“ac”,但原始字符串中没有出现“ac”序列。所以从技术上讲,它不是原始字符串的“子字符串”,尽管它是从原始字母序列派生的排列。
  • c
  • @Curt - 是的,在这种情况下,“aa”将是相同的子字符串
  • @Curt 再次 - 是的,对不起,“所有子字符串”我的意思是保持原始顺序(即按字母顺序)的“所有排列”

标签: c++ algorithm recursion substring sequences


【解决方案1】:

对 Ryan Shaw 上述回答的修正:

不是以二进制计数,而是根据每个字母的数量计算基数中的每个数字。例如:

a d c g h z
3 3 1 1 1 2

算了:

0 0 0 0 0 0
0 0 0 0 0 1
0 0 0 0 0 2
0 0 0 0 1 0
0 0 0 0 1 1 
0 0 0 0 1 2
0 0 0 1 0 0
...
0 0 0 1 1 2
0 0 1 0 0 0
...
0 0 1 1 1 2
0 1 0 0 0 0 
...
0 3 1 1 1 2
1 0 0 0 0 0
...
3 3 1 1 1 2

您已经枚举了所有可能的子集,没有重复。对于其中任何一个,输出字符串只是循环数字并输出指定数量的每个字母的问题。

1 2 0 0 1 1 => addhz
3 0 0 0 1 2 => aaahzz

还有代码:

void GetCounts(const string &source, vector<char> &characters, vector<int> &counts)
{
    characters.clear();
    counts.clear();

    char currentChar = 0;
    for (string::const_iterator iSource = source.begin(); iSource != source.end(); ++iSource)
    {
        if (*iSource == currentChar)
            counts.back()++;
        else
        {
            characters.push_back(*iSource);
            counts.push_back(1);
            currentChar = *iSource;
        }
    }
}

bool Advance(vector<int> &current, const vector<int> &max)
{
    if (current.size() == 0)
        return false;

    current[0]++;
    for (size_t index = 0; index < current.size() - 1 && current[index] > max[index]; ++index)
    {
        current[index] = 0;
        current[index + 1]++;
    }
    if (current.back() > max.back())
        return false;
    return true;
}

string ToString(const vector<int> &current, const vector<char> &characters)
{
    string result;
    for (size_t index = 0; index < characters.size(); ++index)
        for (int i = 0; i < current[index]; ++i)
            result += characters[index];
    return result;
}

int main() { 
    vector<int> max;
    vector<char> characters;

    GetCounts("aaadddcghzz", characters, max);

    vector<int> current(characters.size(), 0);
    int index = 1;
    while (Advance(current, max))
    {
        cout << index++ << ":" << ToString(current, characters) << endl;
    }
}

【讨论】:

  • 这真是太棒了。我本可以考虑这个问题好几天(几周?几个月?),但从来没有想到过这个问题。您是即时编写代码还是已经编写了代码?
  • 我已经多次使用了与 Advance() 函数非常相似的东西。它实际上只是一个带有进位的加法器,并且每个数字都有一个可变的基数。这是一个方便的算法供您使用。
【解决方案2】:

以下是生成所有子序列的递归算法。

/* in C -- I hope it will be intelligible */

#include <stdio.h>

static char input[] = "aaabbbccc";
static char output[sizeof input];

/* i is the current index in the input string
 * j is the current index in the output string
 */
static void printsubs(int i, int j) {
    /* print the current output string */
    output[j] = '\0';
    printf("%s\n", output);
    /* extend the output by each character from each remaining group and call ourselves recursively */
    while(input[i] != '\0') {
        output[j] = input[i];
        printsubs(i + 1, j + 1);
        /* find the next group of characters */
        do ++i;
        while(input[i] == input[i - 1]);
    }
}

int main(void) {
    printsubs(0, 0);
    return 0;
}

如果您的兴趣只是计算有多少子序列,那么您可以更有效地完成这项工作。只需计算每个字母的数量,将每个值加 1,然后将它们相乘。在上面的例子中,有 3 个 a、3 个 b、3 个 c 和 2 个 d,对于 (3 + 1) * (3 + 1) * (3 + 1) * (2 + 1) = 192 个子序列。这样做的原因是您可以在 0 和 3 a、0 和 3 b、0 和 3 c、0 和 2 d 之间进行选择,并且所有这些选择都是独立的

【讨论】:

  • 这没有利用 s[i]
  • 是的。你没有运行它,是吗?
  • 今晚我会试试这个 - 如果它有效,它看起来非常短!
  • 我对此进行了测试 - 它运行良好。事实上,我认为我会接受它作为答案,因为它非常简洁,更符合我的想法。再说一次,我自己永远也想不出这个,感谢上帝!
  • 我输入了“rum”,其中一个子字符串应该是“rm”。
【解决方案3】:

实际上,您的问题是列出给定集合中的所有子集。

考虑到集合 {a,a,a,d,d,d,c,g,h,z,z},您的目标是按顺序列出其所有唯一子集,除了空集: {一种} {一,一} {a,a,a} {a,a,a,d}

有一种快速列出给定集合中所有子集的方法。

我们以{ABC}为例:

{}     = 000
{C}    = 001
{B}    = 010
{BC}   = 011
{A}    = 100
{AC}   = 101
{AB}   = 110
{ABC}  = 111

看到模式了吗?只需使用一个从 0 增长到 2^n - 1 的整数。如果整数的第 i 个数字是 1,则从集合中获取第 i 个元素。

注意:由于在您的示例中,字符串中有重复项;因此,在生成之后,您可能需要删除重复项。

希望对你有帮助。

【讨论】:

【解决方案4】:

好吧,在我看来,一种与您的解决方案类似但与您的输出不匹配的解决方案(不过,请参阅我的 cmets 问题),只是遍历原始字符串的尾部列表(例如,对于“abc”,遍历“abc”、“bc”和“c”),并为每个生成前缀列表(“abc”、“ab”、“a”,然后是“bc”, “b”,然后是“c”)。这与您想要的相比如何?

【讨论】:

    【解决方案5】:

    我使用了这个 java 代码 (http://www.merriampark.com/comb.htm),只得出了 383 个。代码生成了太多的重复项,所以我不得不扔掉很多。我最终只得到了 383 个(请参见下文)。您可能想查看 stl 中 next-combinatiom 的 c++ 代码(但我在任何地方都找不到源代码)。 power set 可能是最好的方法(但你也可能有重复)。

    a
    aa
    aaa
    aaac
    aaacg
    aaacgh
    aaacghz
    aaacghzz
    aaacgz
    aaacgzz
    aaach
    aaachz
    aaachzz
    aaacz
    aaaczz
    aaad
    aaadc
    aaadcg
    aaadcgh
    aaadcghz
    aaadcghzz
    aaadcgz
    aaadcgzz
    aaadch
    aaadchz
    aaadchzz
    aaadcz
    aaadczz
    aaadd
    aaaddc
    aaaddcg
    aaaddcgh
    aaaddcghz
    aaaddcghzz
    aaaddcgz
    aaaddcgzz
    aaaddch
    aaaddchz
    aaaddchzz
    aaaddcz
    aaaddczz
    aaaddd
    aaadddc
    aaadddcg
    aaadddcgh
    aaadddcghz
    aaadddcghzz
    aaadddcgz
    aaadddcgzz
    aaadddch
    aaadddchz
    aaadddchzz
    aaadddcz
    aaadddczz
    aaadddg
    aaadddgh
    aaadddghz
    aaadddghzz
    aaadddgz
    aaadddgzz
    aaadddh
    aaadddhz
    aaadddhzz
    aaadddz
    aaadddzz
    aaaddg
    aaaddgh
    aaaddghz
    aaaddghzz
    aaaddgz
    aaaddgzz
    aaaddh
    aaaddhz
    aaaddhzz
    aaaddz
    aaaddzz
    aaadg
    aaadgh
    aaadghz
    aaadghzz
    aaadgz
    aaadgzz
    aaadh
    aaadhz
    aaadhzz
    aaadz
    aaadzz
    aaag
    aaagh
    aaaghz
    aaaghzz
    aaagz
    aaagzz
    aaah
    aaahz
    aaahzz
    aaaz
    aaazz
    aac
    aacg
    aacgh
    aacghz
    aacghzz
    aacgz
    aacgzz
    aach
    aachz
    aachzz
    aacz
    aaczz
    aad
    aadc
    aadcg
    aadcgh
    aadcghz
    aadcghzz
    aadcgz
    aadcgzz
    aadch
    aadchz
    aadchzz
    aadcz
    aadczz
    aadd
    aaddc
    aaddcg
    aaddcgh
    aaddcghz
    aaddcghzz
    aaddcgz
    aaddcgzz
    aaddch
    aaddchz
    aaddchzz
    aaddcz
    aaddczz
    aaddd
    aadddc
    aadddcg
    aadddcgh
    aadddcghz
    aadddcghzz
    aadddcgz
    aadddcgzz
    aadddch
    aadddchz
    aadddchzz
    aadddcz
    aadddczz
    aadddg
    aadddgh
    aadddghz
    aadddghzz
    aadddgz
    aadddgzz
    aadddh
    aadddhz
    aadddhzz
    aadddz
    aadddzz
    aaddg
    aaddgh
    aaddghz
    aaddghzz
    aaddgz
    aaddgzz
    aaddh
    aaddhz
    aaddhzz
    aaddz
    aaddzz
    aadg
    aadgh
    aadghz
    aadghzz
    aadgz
    aadgzz
    aadh
    aadhz
    aadhzz
    aadz
    aadzz
    aag
    aagh
    aaghz
    aaghzz
    aagz
    aagzz
    aah
    aahz
    aahzz
    aaz
    aazz
    ac
    acg
    acgh
    acghz
    acghzz
    acgz
    acgzz
    ach
    achz
    achzz
    acz
    aczz
    ad
    adc
    adcg
    adcgh
    adcghz
    adcghzz
    adcgz
    adcgzz
    adch
    adchz
    adchzz
    adcz
    adczz
    add
    addc
    addcg
    addcgh
    addcghz
    addcghzz
    addcgz
    addcgzz
    addch
    addchz
    addchzz
    addcz
    addczz
    addd
    adddc
    adddcg
    adddcgh
    adddcghz
    adddcghzz
    adddcgz
    adddcgzz
    adddch
    adddchz
    adddchzz
    adddcz
    adddczz
    adddg
    adddgh
    adddghz
    adddghzz
    adddgz
    adddgzz
    adddh
    adddhz
    adddhzz
    adddz
    adddzz
    addg
    addgh
    addghz
    addghzz
    addgz
    addgzz
    addh
    addhz
    addhzz
    addz
    addzz
    adg
    adgh
    adghz
    adghzz
    adgz
    adgzz
    adh
    adhz
    adhzz
    adz
    adzz
    ag
    agh
    aghz
    aghzz
    agz
    agzz
    ah
    ahz
    ahzz
    az
    azz
    c
    cg
    cgh
    cghz
    cghzz
    cgz
    cgzz
    ch
    chz
    chzz
    cz
    czz
    d
    dc
    dcg
    dcgh
    dcghz
    dcghzz
    dcgz
    dcgzz
    dch
    dchz
    dchzz
    dcz
    dczz
    dd
    ddc
    ddcg
    ddcgh
    ddcghz
    ddcghzz
    ddcgz
    ddcgzz
    ddch
    ddchz
    ddchzz
    ddcz
    ddczz
    ddd
    dddc
    dddcg
    dddcgh
    dddcghz
    dddcghzz
    dddcgz
    dddcgzz
    dddch
    dddchz
    dddchzz
    dddcz
    dddczz
    dddg
    dddgh
    dddghz
    dddghzz
    dddgz
    dddgzz
    dddh
    dddhz
    dddhzz
    dddz
    dddzz
    ddg
    ddgh
    ddghz
    ddghzz
    ddgz
    ddgzz
    ddh
    ddhz
    ddhzz
    ddz
    ddzz
    dg
    dgh
    dghz
    dghzz
    dgz
    dgzz
    dh
    dhz
    dhzz
    dz
    dzz
    g
    gh
    ghz
    ghzz
    gz
    gzz
    h
    hz
    hzz
    z
    zz
    

    【讨论】:

    • 第384个结果是空字符串。
    猜你喜欢
    • 2021-07-30
    • 2012-10-23
    • 1970-01-01
    • 2016-04-20
    • 2022-11-04
    • 1970-01-01
    • 2016-01-12
    • 1970-01-01
    • 2016-10-18
    相关资源
    最近更新 更多