找到将一个二进制字符串更改为另一个字符串所需的最小步骤

时间:2017-09-15 06:57:00

标签: string algorithm

  

给定两个字符串str1和str2,它们只包含01   是将str1更改为str2的一些步骤,

     

步骤1:找到长度为2的str1子串并反转子串,str1变为str1' (str1'!= str1)

     

第二步:找到str1的子字符串'长度为3,并反转子串,str1'成为str1'' (str1''!= str1')

     

以下步骤类似。

     

字符串长度在[2,30]

范围内      

要求:每个步骤必须执行一次,我们无法跳过   前面的步骤并执行下一步。

     

如果可以将str1更改为str2,则输出所需的最小步数,否则输出-1

示例1

  

str1 =" 1010",str2 =" 0011",所需的最小步骤为2

     

首先,选择范围[2,3]," 1010"中的子字符串; - > " 1001",

     

然后选择范围[0,2]," 1001"中的子串。 - > " 0011"

示例2

  

str1 =" 1001",str2 =" 0110",无法将str1更改为str2,   因为在步骤1中,str1可以改为" 0101"或者" 1010",但是在步骤3中,不可能改变长度3子串以使其不同。所以输出是-1。

示例3

  

str1 =" 10101010",str2 =" 00101011",输出为7

我无法弄清楚示例3,因为有两种可能性。任何人都可以提供一些如何解决这个问题的提示吗?这是什么类型的 问题?它是动态编程吗?

3 个答案:

答案 0 :(得分:0)

这实际上是一个动态编程问题。为了解决这个问题,我们将尝试所有可能的排列,但要沿途记住结果。似乎有太多的选择-2^30个长度为30的不同二进制字符串,但是请记住,还原字符串不会更改零和1的数目,因此上限是实际上,30 choose 15 = 155117520包含15个零和一的字符串。大约1.5亿个可能的结果还不错。

因此,从我们的start字符串开始,我们将从到目前为止派生的每个字符串中获取所有可能的字符串,直到生成end字符串。我们还将跟踪前辈以重建世代。这是我的代码:

start = '10101010'
end = '00101011'

dp = [{} for _ in range(31)]
dp[1][start] = '' # Originally only start string is reachable

for i in range(2, len(start) + 1):
  for s in dp[i - 1].keys():
    # Try all possible reversals for each string in dp[i - 1]
    for j in range(len(start) - i + 1):
      newstr = s
      newstr = newstr[:j] + newstr[j:j+i][::-1] + newstr[j+i:]
      dp[i][newstr] = s
  if end in dp[i]:
    ans = []
    cur = end
    for j in range(i, 0, -1):
      ans.append(cur)
      cur = dp[j][cur]
    print(ans[::-1])
    exit(0)

print('Impossible!')

在您的第三个示例中,这给出了序列['10101010', '10101001', '10101100', '10100011', '00101011']-从str1到str2。如果检查字符串之间的差异,您将看到进行了哪些转换。因此,此转换可以分4个步骤完成,而不是您建议的7个步骤。

最后,这在python中要慢30倍,但是如果将其重写为C ++,将需要几秒钟的时间。

答案 1 :(得分:0)

可以使用广度优先搜索解决此问题。以下解决方案使用一个队列,该队列存储具有当前字符串作为第一成员和当前操作长度(最初为2)作为第二成员的一对。集合用于存储已经访问过的字符串,以防止进入冗余状态。对于当前字符串,我们反转每个长度为k的子字符串,其中k为当前操作长度,如果以前没有看到过,则将其添加到队列中。如果当前字符串等于所需字符串,则答案为“当前操作长度2”。如果队列为空,则不可能回答。

string str1,str2;
cin>>str1>>str2;

queue<pair<string, int>> q;
set<string> s;

q.push({str1,2});
s.insert(str1);
while(!q.empty())
{
    auto p=q.front();
    q.pop();
    if(p.first==str2)
    {
        cout<<p.second-2;
        return 0;
    }
    if(p.second<=p.first.size())
    {
        for(int i=0;i<=p.first.size()-p.second;i++)
        {
            string x=p.first;
            reverse(x.begin()+i,x.begin()+i+p.second);
            if(s.find(x)==s.end())
            {
                q.push({x,p.second+1});
                s.insert(x);
            }

        }
    }
}
cout<<-1;

答案 2 :(得分:-3)

将str1保存为BFS的开始,在每一步,反转所有长度为2和3的子串的值,看看反转后形成的新字符串之前是否见过.....如果没见过... .将它们推入队列并保持步数……如果队列前面的字符串在任何时候都是 str2 ……那一步就是答案