使用分而治之的方法进行推理
Posted
技术标签:
【中文标题】使用分而治之的方法进行推理【英文标题】:Reasoning behind using a divide and conquer approach 【发布时间】:2021-06-02 07:00:11 【问题描述】:我正在尝试解决 LeetCode 上的this question:
如果
s
包含的每个字母表都以大写和小写形式出现,则字符串s
很好。例如,“abABB”很好,因为出现了“A”和“a”,出现了“B”和“b”。但是,“abA”并不是因为出现了“b”,而是“B”没有出现。 给定一个字符串s
,返回s
的最长子字符串,这很好。如果有多个,则返回最早出现的子字符串。如果没有,则返回一个空字符串。 对于 s = "YazaAay",预期输出为:"aAa"
其中一个top voted solutions 使用分而治之的方法:
class Solution
public String longestNiceSubstring(String s)
if (s.length() < 2) return "";
char[] arr = s.toCharArray();
Set<Character> set = new HashSet<>();
for (char c: arr) set.add(c);
for (int i = 0; i < arr.length; i++)
char c = arr[i];
if (set.contains(Character.toUpperCase(c)) && set.contains(Character.toLowerCase(c))) continue;
String sub1 = longestNiceSubstring(s.substring(0, i));
String sub2 = longestNiceSubstring(s.substring(i+1));
return sub1.length() >= sub2.length() ? sub1 : sub2;
return s;
我了解它的工作原理,但不了解使用分而治之方法背后的直觉。换句话说,如果我在忘记一切之后的几天/几周后再重新审视这个问题,我将无法意识到这是一个分而治之的问题。
是什么“东西”使它可以通过分而治之的方法解决?
【问题讨论】:
总的来说,分而治之非常适合复杂的场景,因为您可以轻松地在多个线程上运行代码。 (顺便说一句:这个代码示例中的递归方法调用看起来很邪恶,我认为如果输入太大,这可能会导致 ***Error) @BenjaminM,是的,在这种情况下,问题约束表明限制不是太高:1 <= s.length <= 100
。
【参考方案1】:
这就是算法可以用简单的英语描述的方式:
如果整个字符串都很好,我们就完成了。
否则,必须有一个字符仅在一种情况下存在。这样的字符自然地将字符串划分为两个子字符串。 征服他们每个人,并比较结果。
编辑:顺便说一句,我不认为这是 D&C 问题的一个很好的例子。关键是,一旦我们遇到第一个“坏”字符,它左边的子字符串就很好了。没有必要深入其中。只需记录它的长度并继续前进。它是一个简单的循环。
【讨论】:
留给第一个坏字符的子字符串不一定总是好的。例如:ApzPAa。这里z
是第一个坏字符。但是,它左边的子字符串不好。
@AKSingh 感谢您的关注。我不知道我在想什么。
不用担心。这可能发生在我们最好的人身上。
添加一些修剪以节省一些精力:如果您有一个不错的子字符串,则不需要检查任何比这更短的尚未检查的子字符串。【参考方案2】:
用***的话说,当一个问题可以分解为“2 个或更多子问题”时,分而治之是最合适的。这里的解决方案检查输入字符串是否满足条件,然后在每个字符处将其一分为二,并递归检查字符串是否满足条件,直到没有解决方案。一般来说,分而治之的应用很容易感觉到什么时候可以对称地细分问题,例如在用于计算一组点的 delaunay 三角剖分的 DeWall 算法中(http://vcg.isti.cnr.it/publications/papers/dewall.pdf - 很酷的东西)。
在这种情况下,子字符串问题的不同之处在于它通过增加细分行来检查 all(编辑:) possible 可行的细分。为了澄清任何可能感到困惑的人,这是必要的,因为字符串不能从中间拆分,否则您可能会拆分像“aAaA”这样的子字符串,最后只返回一半。这种在“两个或多个问题”中满足更多条件,但我同意在这种情况下它不直观。
希望这会有所帮助,我最近在实现引用的算法时不得不了解很多。有更多经验的人可能会有更好的答案。
【讨论】:
它不检查 all 细分。它只检查可行的。 我指的是这一行:for (int i = 0; i < arr.length; i++)
- 当然,如果你想挑剔的话,它会检查所有细分的点。
... 但仅当该点仅存在于单个案例中时才会递归。请注意那个很长的set.contains(Character.toUpperCase(c)) && set.contains(Character.toLowerCase(c))
测试中的continue
。
哎呀,没看到(也从未接触过 java),但经过一番谷歌搜索后,我想我明白了你的意思 - 我会编辑它以上是关于使用分而治之的方法进行推理的主要内容,如果未能解决你的问题,请参考以下文章