扰乱字符串
难度:困难
题目描述
使用下面描述的算法可以扰乱字符串 s
得到字符串 t
:
- 如果字符串的长度为 1 ,算法停止
- 如果字符串的长度 > 1 ,执行下述步骤:
- 在一个随机下标处将字符串分割成两个非空的子字符串。即,如果已知字符串
s
,则可以将其分成两个子字符串x
和y
,且满足s = x + y
。 - 随机 决定是要「交换两个子字符串」还是要「保持这两个子字符串的顺序不变」。即,在执行这一步骤之后,
s
可能是s = x + y
或者s = y + x
。 - 在
x
和y
这两个子字符串上继续从步骤 1 开始递归执行此算法。
- 在一个随机下标处将字符串分割成两个非空的子字符串。即,如果已知字符串
给你两个 长度相等 的字符串 s1
和 s2
,判断 s2
是否是 s1
的扰乱字符串。如果是,返回 true
;否则,返回 false
。
示例1
输入: s1 = "great", s2 = "rgeat"
输出: true
示例2
输入: s1 = "abcde", s2 = "caebd"
输出: false
示例3
输入: s1 = "a", s2 = "a"
输出: true
题解
直接使用动态规划来解题,原始的递归方法中存在大量的重复操作,从而使时间复杂度大幅提高,这时可以使用一个三维数组dp[i][j][k]
来记录,i
表示s1
起始位置,j
表示s2
起始位置,k
表示当前字符串的长度
转移方程如下:
dp[i,j,k] = (dp[i,j,m] && dp[i+m,j+m,k-m]) || (dp[i,j+k-m,m] && dp[i+m,j,k-m])
其中m的取值范围是[1,k)。这表示切割位置可以在1到k之间选择。
想法代码
csharp
using System;
using System.Net.Http.Headers;
class Solution
{
List<List<string>> strings = new List<List<string>>();
public static void Main(string[] args)
{
string s1 = "great";
string s2 = "rgeat";
Solution solution = new Solution();
bool ans = solution.IsScramble(s1, s2);
Console.WriteLine(ans);
}
public bool IsScramble(string s1, string s2)
{
if (s1.Equals(s2))
{
return true;
}
int length = s1.Length;
int[,,] dp = new int[length, length, length + 1];
for (int k = 1; k <= length; k++)
{
for (int i = 0; i <= length - k; i++)
{
for (int j = 0; j <= length - k; j++)
{
if (k == 1)
{
dp[i, j, k] = s1[i] == s2[j] ? 1 : 0;
continue;
}
for (int m = 1; m < k; m++)
{
if ((dp[i, j, m] == 1 && dp[i + m, j + m, k - m] == 1) || (dp[i, j + k - m, m] == 1 && dp[i + m, j, k - m] == 1))
{
dp[i, j, k] = 1;
break;
}
}
}
}
}
return dp[0, 0, length] == 1;
}
}