检查两个字符串数组是否相等 图解题解
这道题到底在问什么
- 输入
- word1 = ["ab", "c"], word2 = ["a", "bc"]
- 输出
- true(两边都拼成 "abc")
- 输入
- word1 = ["a", "cb"], word2 = ["ab", "c"]
- 输出
- false("acb" 与 "abc" 第二个字符就不同)
- 输入
- word1 = ["abc", "d", "defg"], word2 = ["abcddefg"]
- 输出
- true(都拼成 "abcddefg",一个分三段一个只一段)
最优解:一步一步想明白
- 3记牢这句话:数组怎么切不重要,先各自拼成完整字符串,再整串对整串地比。下面每一帧都在做拼接或者比对这两件事。
- 4先看舞台。上面这一行准备放 word1 拼出来的字符串 s1,下面这一行准备放 word2 拼出来的字符串 s2。右边面板列着两个数组各自由哪些词组成。现在两行都还是灰的,代表都没正式拼好。接下来我先把上行 s1 一段一段拼出来,再拼下行 s2,最后逐列比对。
- 5把 word1 的第 0 个元素 "ab" 接到上行后面,它有 2 个字符,依次放进第 0 到第 1 格。橙色就是这一帧刚接上的部分,灰蓝色是之前已经拼好的。到这里上行 s1 已经是 "ab"。
- 6把 word1 的第 1 个元素 "c" 接到上行后面,它只有一个字符,放进第 2 格。橙色就是这一帧刚接上的部分,灰蓝色是之前已经拼好的。到这里上行 s1 已经是 "abc"。
- 7把 word1 的第 2 个元素 "de" 接到上行后面,它有 2 个字符,依次放进第 3 到第 4 格。橙色就是这一帧刚接上的部分,灰蓝色是之前已经拼好的。到这里上行 s1 已经是 "abcde"。
- 8上行三段都接完了,word1 = ["ab", "c", "de"] 顺次相连拼成了完整的 s1 = "abcde",一共 5 个字符。注意拼接只看顺序、不看原来怎么分段。上行就位,下面去拼下行 s2。
- 9换下行。把 word2 的第 0 个元素 "a" 接到下行后面,它只有一个字符,放进第 0 格。橙色是本帧刚接上的部分。到这里下行 s2 已经是 "a"。你会发现 word2 的分段方式和 word1 完全不同,但拼出来的样子越来越像。
- 10换下行。把 word2 的第 1 个元素 "bcde" 接到下行后面,它有 4 个字符,依次放进第 1 到第 4 格。橙色是本帧刚接上的部分。到这里下行 s2 已经是 "abcde"。你会发现 word2 的分段方式和 word1 完全不同,但拼出来的样子越来越像。
- 11下行也拼完了,word2 = ["a", "bcde"] 拼成了 s2 = "abcde"。现在两行都摆好:上行 s1 = "abcde",下行 s2 = "abcde"。一个数组切成三段、一个切成两段,拼出来居然长得一模一样,到底是不是真的一样,得一列一列对照着验。
- 12正式比之前先看长度。s1 有 5 个字符,s2 也有 5 个字符,长度相等。这一步很关键:如果两个串长度都不一样,那根本不可能相等,可以立刻返回 false,连逐字符都省了。这里长度相同,说明有得比,下面从第 0 列开始一列一列地对字符。
- 13把目光对到第 0 列,紫色高亮的就是要比的这一对。上行这一格是 s1 的第 0 个字符 "a",下行这一格是 s2 的第 0 个字符 "a"。它俩相不相同?下一帧揭晓。左边已经比过的列会标成绿色,绿色越铺越长就说明前面都对得上。
- 14"a" 和 "a" 是同一个字符,这一列对上了,两格一起变绿。已经对上 1 列,还差 4 列,继续往右挪到第 1 列。
- 15把目光对到第 1 列,紫色高亮的就是要比的这一对。上行这一格是 s1 的第 1 个字符 "b",下行这一格是 s2 的第 1 个字符 "b"。它俩相不相同?下一帧揭晓。前面 1 列已经全绿,代表都已对上。
- 16"b" 和 "b" 是同一个字符,这一列对上了,两格一起变绿。已经对上 2 列,还差 3 列,继续往右挪到第 2 列。
- 17把目光对到第 2 列,紫色高亮的就是要比的这一对。上行这一格是 s1 的第 2 个字符 "c",下行这一格是 s2 的第 2 个字符 "c"。它俩相不相同?下一帧揭晓。前面 2 列已经全绿,代表都已对上。
- 18"c" 和 "c" 是同一个字符,这一列对上了,两格一起变绿。已经对上 3 列,还差 2 列,继续往右挪到第 3 列。
- 19把目光对到第 3 列,紫色高亮的就是要比的这一对。上行这一格是 s1 的第 3 个字符 "d",下行这一格是 s2 的第 3 个字符 "d"。它俩相不相同?下一帧揭晓。前面 3 列已经全绿,代表都已对上。
- 20"d" 和 "d" 是同一个字符,这一列对上了,两格一起变绿。已经对上 4 列,还差 1 列,继续往右挪到第 4 列。
- 21把目光对到第 4 列,紫色高亮的就是要比的这一对。上行这一格是 s1 的第 4 个字符 "e",下行这一格是 s2 的第 4 个字符 "e"。它俩相不相同?下一帧揭晓。前面 4 列已经全绿,代表都已对上。
- 22"e" 和 "e" 是同一个字符,这一列对上了,两格一起变绿。这是最后一列,也对上了,全部 5 列都通过。
- 23五列全绿,意味着 s1 和 s2 长度相等、而且每一个字符都对得上,这两个字符串完全相同,所以返回 true。回头看,word1 切成三段、word2 切成两段,切法天差地别,可一旦拼成整串再比,答案清清楚楚。
- 24把整个过程回放一遍:先把 word1 顺次拼成 s1 = "abcde",再把 word2 顺次拼成 s2 = "abcde",然后比长度发现都是 5,再一列一列比字符,五列全部相同。结论就是两个数组表示的字符串相等,返回 true。整个过程把每个字符只碰了常数次,既直观又高效。
⚠️ 容易写错的地方
✗ 错:按数组下标一一对应比较 word1[i] 和 word2[i]
✓ 对:先各自拼成完整字符串再整串比较
两个数组的切分方式可能完全不同,["ab","c"] 和 ["a","bc"] 下标对不齐却表示同一个串,逐元素比会把相等的判成不相等
✗ 错:只比长度相等就以为两串相等,或忘了先比长度
✓ 对:长度不等可直接返回 false;长度相等仍要逐字符比
长度相等只是必要条件,内容仍可能某一位不同;但长度一旦不等就肯定不相等,先比长度能在不等时立刻收工
✗ 错:Java 里用两个等号比较两个字符串是否相等
✓ 对:Java 比字符串内容要用 equals
Java 的两个等号比的是对象引用是不是同一个,不是内容;内容相等但不是同一个对象时会被错判成不相等,必须用 equals
完整代码(Python / C++ / Java)
Python
from __future__ import annotations
from typing import *
from collections import *
from functools import *
from itertools import *
from math import *
from heapq import *
from bisect import *
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def arrayStringsAreEqual(self, word1: List[str], word2: List[str]) -> bool:
return ''.join(word1) == ''.join(word2)C++
#include <algorithm>
#include <array>
#include <cctype>
#include <climits>
#include <cmath>
#include <deque>
#include <functional>
#include <iostream>
#include <limits>
#include <map>
#include <numeric>
#include <optional>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
struct ListNode {
int val;
ListNode *next;
ListNode() : val(0), next(nullptr) {}
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode *next) : val(x), next(next) {}
};
class Solution {
public:
bool arrayStringsAreEqual(vector<string>& word1, vector<string>& word2) {
string s1, s2;
for (const string& w : word1) s1 += w;
for (const string& w : word2) s2 += w;
return s1 == s2;
}
};Java
import java.util.*;
class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(){} TreeNode(int val){this.val=val;} TreeNode(int val, TreeNode left, TreeNode right){this.val=val;this.left=left;this.right=right;} }
class ListNode { int val; ListNode next; ListNode(){} ListNode(int val){this.val=val;} ListNode(int val, ListNode next){this.val=val;this.next=next;} }
class Solution {
public boolean arrayStringsAreEqual(String[] word1, String[] word2) {
return String.join("", word1).equals(String.join("", word2));
}
}复杂度
时间
O(n)
n 是两个数组里所有字符的总数。拼接要把每个字符各扫一遍是 O(n),比较两个串再扫一遍也是 O(n),合计还是 O(n)
空间
O(n)
这套解法把两个数组各自拼成一个完整字符串,峰值要同时存住这两个长度合计为 O(n) 的串,所以额外空间是 O(n)。如果改用两个指针在原数组上流式逐字符比较、不预先拼接,额外空间可以降到 O(1)
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 检查两个字符串数组是否相等 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
这道题最直接的写法是什么,为什么一定正确?+
最直接的就是把 word1 顺次拼成一个字符串、把 word2 顺次拼成另一个字符串,然后整串比较是否相等。它一定正确,是因为题目对「数组表示的字符串」的定义就是把所有元素按顺序连接,数组怎么切分纯属表象,拼完之后的整串才是唯一要比较的对象。两边都还原成整串,比较就有了统一基准。
面试官追问能不能不额外建字符串、做到 O(1) 空间?+
可以,用双指针流式比较。一个指针 i 走 word1、一个指针 j 走 word2,各自再配一个内层下标指向当前词里的第几个字符。每一步取出两边当前的字符比一下,相同就各自往前挪一位,某个词走到末尾就跳到下一个词。一旦遇到不同的字符立即返回 false,如果两边同时全部走完说明完全相等返回 true。这样全程不另建字符串,额外空间只有几个指针,是 O(1),而且能在第一处不同就提前停。
这类题属于什么套路,以后怎么认?+
它属于「把结构化输入归一化成规范形式再比较」这一类:输入被切成了若干块,但真正要比的是它们拼起来的整体,先归一化成完整串,问题就变简单了。它也和「双指针同步遍历两个序列」相关,当不想付出额外空间时就改成两个指针并排走。认这类题就看一点:要比较的对象是不是被人为切分过,如果是,先还原再比。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄声线把 检查两个字符串数组是否相等 一步步讲透(全站 0 节语音精讲,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。