交换一些字符后具有最大给定子串数的字符串?

时间:2015-06-12 21:08:48

标签: c++ string algorithm

所以,这是我正在经历的面试问题。

我有字符串a, b, and c。我希望通过交换k中的一些字母来获取字符串a,以便k包含与bc相等的非重叠子字符串尽可能。字符串x的子字符串是由x的连续字符段形成的字符串。如果字符x中的位置i被两个字符集占用,则字符串x的两个子字符串会重叠。

输入:第一行包含字符串a,第二行包含字符串b,第三行包含字符串c(1≤| a |,| b |,| c |≤10^ 5,其中| s |表示字符串的长度s)。

所有三个字符串仅由小写英文字母组成。

b和c可能重合。

输出:找到一个可能的字符串k。

Example: 
I/P 
abbbaaccca
ab
aca

O/P
ababacabcc
this optimal solutions has three non-overlaping substrings equal to either b or c on positions 1 – 2 (ab), 3 – 4 (ab), 5 – 7 (aca).

现在,我能想到的方法是为每个字符串创建一个字符计数数组,然后继续前进。基本上,迭代原始字符串(a),检查b和c的出现。如果不存在,请交换尽可能多的字符以生成b或c(以较短者为准)。但是,显然这不是最佳方法。 谁能提出更好的建议? (只有伪代码就足够了) 谢谢!

3 个答案:

答案 0 :(得分:1)

首先,您需要做的是计算每个字符串的每个字符的出现次数。 a的出现次数将是您的背包,您需要填写bc个。{/ p>

请注意,当我说背包时,我指的是a的字符数向量,将b插入a意味着将a的字符数向量减少字符数向量b。 我的数学证明我有点短暂,但你需要

  1. 尽可能多地b插入背包

  2. 尽可能多地将c插入背包(在1之后留下的空间)。

  3. 如果从背包中移除b将允许插入更多c,请从背包中移除b。否则,完成。

  4. 尽可能多地填充c背包

  5. 重复3-4。

  6. 整个程序计算背包中的b和c的数量,输出应为:

    [b_count times b][c_count times c][char_occurrence_left_in_knapsack_for_char_x times char_x for each char_x in lower_case_english]
    

    这应该可以解决你在O(n)的问题。

答案 1 :(得分:0)

假设允许的字符有ASCII码0-127,我会编写一个函数来计算字符串中每个字符的出现次数:

int[] count(String s) {
    int[] res = new int[128];
    for(int i=0; i<res.length(); i++)
        res[i] = 0;
    for(int i=0; i<a.length(); i++)
        res[i]++;
    return res;
}

我们现在可以计算每个字符串中的出现次数:

int aCount = count(a);
int bCount = count(b);
int cCount = count(c);

然后我们可以编写一个函数来计算字符串可以从另一个字符串的字符中划分出来的次数:

int carveCount(int[] strCount, int[] subStrCount) {
    int min = Integer.MAX_VALUE;
    for(int i=0; i<subStrCount.length(); i++) {
        if (subStrCount[i] == 0)
            continue;
        if (strCount[i] >= subStrCount[i])
            min = Math.min(min, strCount[i]-subStrCount[i]);
        else {
            return 0;
        }
    }
    for(int i=0; i<subStrCount.length(); i++) {
        if (subStrCount[i] != 0)
            strStrCount[i] -= min;
    }
    return min;
}

并调用函数:

int bFitCount = carve(aCount, bCount);
int cFitCount = carve(aCount, cCount);

编辑:我没有意识到你想要的所有角色都在这里修复。

最后,要产生输出:

StringBuilder sb = new StringBuilder();
for(int i=0; i<bFitCount; i++) {
    sb.append(b);
for(int i=0; i<cFitCount; i++) {
    sb.append(c);
for(int i=0; i<aCount.length; i++) {
    for(int j=0; j<aCount[i]; j++) 
        sb.append((char)i);
}
return sb.toString();

还有一条评论:如果目标是最大化重复次数(b)+重复次数(c),那么如果c更短,您可能需要首先擦拭b和c。这样,如果他们分享一些角色,你就有更大的机会增加结果。

算法可以进一步优化,但因为它应该具有复杂度O(n),其中n是三个字符串长度的总和。

答案 2 :(得分:0)

相关问题称为Knapsack problem。 这基本上是@Tal Shalti描述的解决方案。 我试图保持一切可读性。

我的程序返回abbcabacac作为出现次数最多的字符串之一(3)。

要在不重复排列的情况下获取所有排列,请使用std::next_permutation中的algorithm。主要功能没有太多发生。如果实现了更多的出现次数,我只存储出现次数和置换次数。

int main()
{
    std::string word = "abbbaaccca";
    std::string patternSmall = "ab";
    std::string patternLarge = "aca";


    unsigned int bestOccurrence = 0;
    std::string bestPermutation = "";


    do {
        // count and remove occurrence
        unsigned int occurrences = FindOccurences(word, patternLarge, patternSmall);

        if (occurrences > bestOccurrence) {
            bestOccurrence = occurrences;
            bestPermutation = word;

            std::cout << word << " .. " << occurences << std::endl;
        }


    } while (std::next_permutation(word.begin(), word.end()));


    std::cout << "Best Permutation " << bestPermutation << "  with " << bestOccurrence << " occurrences." << std::endl;

    return 0;
}

此函数处理基本算法。 pattern1是较长的模式,因此将在最后搜索。如果找到一个模式,它将被替换为字符串&#34; @@&#34;,因为这在英语中应该是非常罕见的。 变量occurrenceCounter会跟踪找到的出现次数。

unsigned int FindOccurrences(const std::string& word, const std::string& pattern1, const std::string& pattern2)
{
    unsigned int occurrenceCounter = 0;

    std::string tmpWord(word);

    // '-1' makes implementation of while() easier
    std::string::size_type i = -1;

    i = -1;
    while (FindPattern(tmpWord, pattern2, ++i)) {
        occurrenceCounter++;
        tmpWord.replace(tmpWord.begin() + i, tmpWord.begin() + i + pattern2.size(), "@@");
    }

    i = -1;
    while (FindPattern(tmpWord, pattern1, ++i)) {
        occurrenceCounter++;
        tmpWord.replace(tmpWord.begin() + i, tmpWord.begin() + i + pattern1.size(), "@@");
    }

    return occurrenceCounter;
}

此函数返回找到的模式的第一个位置。如果找不到模式,则std::string::npos会返回string.find(...)。此外,string.find(...)开始搜索以索引i开头的模式。

bool FindPattern(const std::string& word, const std::string& pattern, std::string::size_type& i)
{
      std::string::size_type foundPosition = word.find(pattern, i);

      if (foundPosition == std::string::npos) {
         return false;
      }

      i = foundPosition;

     return true;
}