题目地址:
https://leetcode.com/problems/greatest-common-divisor-of-strings/
给定两个字符串 s s s和 t t t,如果存在字符串 x x x使得 s = x + x + . . . + x s=x+x+...+x s=x+x+...+x并且 t = x + x + . . . + x t=x+x+...+x t=x+x+...+x,也就是说 s s s和 t t t都可以分割为某个相同字符串的拼接,那就返回最长满足条件的 x x x,否则返回空串。题目要求分割出 x x x的出现次数要大于等于 1 1 1,也就是说不能分出空串。我们可以将 x x x称为“最大公约串”。
首先,如果其中一个为空则直接返回空串。如果 s + t ≠ t + s s+t\ne t+s s+t=t+s也直接返回空串。然后算一下两个串长度的最大公约数 l l l,则 s s s的长 l l l的前缀即为所求。
算法正确性证明:
首先,如果
s
+
t
≠
t
+
s
s+t\ne t+s
s+t=t+s,那么肯定不会有最大公约串,这是显然的,所以算法正确。设
s
+
t
=
t
+
s
s+t=t+s
s+t=t+s,并且两串的长度的最大公约数等于
l
l
l,如果两个串长度相等,那显然成立;否则的话,不妨设
s
s
s短,如果
l
s
∣
l
t
l_s|l_t
ls∣lt,容易证明成立。接着,我们只需要证明
s
[
0
:
l
−
1
]
s[0:l-1]
s[0:l−1]是最大公约串即可。为了方便起见,规定
s
0
l
=
s
[
0
:
l
−
1
]
,
s
1
l
=
s
[
l
:
2
l
−
1
]
,
.
.
.
s_{0l}=s[0:l-1],s_{1l}=s[l:2l-1],...
s0l=s[0:l−1],s1l=s[l:2l−1],...,对
t
t
t也做类似规定。证明思路是利用字符串哈希。设
l
=
gcd
(
l
s
,
l
t
)
l=\gcd(l_s,l_t)
l=gcd(ls,lt),并且
l
s
=
u
l
l_s=ul
ls=ul,
l
t
=
v
l
l_t=vl
lt=vl(即有
gcd
(
u
,
v
)
=
1
\gcd(u,v)=1
gcd(u,v)=1),令
s
k
l
s_{kl}
skl的哈希值表示为
s
k
s_k
sk,
t
k
l
t_{kl}
tkl的哈希值表示为
t
k
t_k
tk,哈希用的素数要取的足够大,以至于两个哈希值相等就等价于字符串相等。考虑整系数多项式环
Z
[
X
]
\mathbb{Z}[X]
Z[X],那么
s
+
t
=
t
+
s
s+t=t+s
s+t=t+s等价于:
s
0
+
s
1
X
+
s
2
X
2
+
.
.
.
+
s
u
−
1
X
u
−
1
+
X
u
(
t
0
+
t
1
X
+
.
.
.
+
t
v
−
1
X
v
−
1
)
=
t
0
+
t
1
X
+
t
2
X
2
+
.
.
.
+
t
v
−
1
X
v
−
1
+
X
v
(
s
0
+
s
1
X
+
.
.
.
+
s
u
−
1
X
u
−
1
)
s_0+s_1X+s_2X^2+...+s_{u-1}X^{u-1}+\\X^u(t_0+t_1X+...+t_{v-1}X^{v-1})=\\t_0+t_1X+t_2X^2+...+t_{v-1}X^{v-1}+\\X^v(s_0+s_1X+...+s_{u-1}X^{u-1})
s0+s1X+s2X2+...+su−1Xu−1+Xu(t0+t1X+...+tv−1Xv−1)=t0+t1X+t2X2+...+tv−1Xv−1+Xv(s0+s1X+...+su−1Xu−1)这个式子说白了就是对应的长度
l
l
l的片段相等。上面的式子等价于:
(
X
u
−
1
)
(
t
0
+
t
1
X
+
t
2
X
2
+
.
.
.
+
t
v
−
1
X
v
−
1
)
=
(
X
v
−
1
)
(
s
0
+
s
1
X
+
.
.
.
+
s
u
−
1
X
u
−
1
)
(X^u-1)(t_0+t_1X+t_2X^2+...+t_{v-1}X^{v-1})=\\(X^v-1)(s_0+s_1X+...+s_{u-1}X^{u-1})
(Xu−1)(t0+t1X+t2X2+...+tv−1Xv−1)=(Xv−1)(s0+s1X+...+su−1Xu−1)两边同时除以
X
−
1
X-1
X−1得:
(
1
+
X
+
.
.
.
+
X
u
−
1
)
(
t
0
+
t
1
X
+
t
2
X
2
+
.
.
.
+
t
v
−
1
X
v
−
1
)
=
(
1
+
X
+
.
.
.
+
X
v
−
1
)
(
s
0
+
s
1
X
+
.
.
.
+
s
u
−
1
X
u
−
1
)
(1+X+...+X^{u-1})(t_0+t_1X+t_2X^2+...+t_{v-1}X^{v-1})\\=(1+X+...+X^{v-1})(s_0+s_1X+...+s_{u-1}X^{u-1})
(1+X+...+Xu−1)(t0+t1X+t2X2+...+tv−1Xv−1)=(1+X+...+Xv−1)(s0+s1X+...+su−1Xu−1)比较常数项得
s
0
=
t
0
s_0=t_0
s0=t0。由于
gcd
(
u
,
v
)
=
1
\gcd(u,v)=1
gcd(u,v)=1,所以
1
+
X
+
.
.
.
+
X
u
−
1
1+X+...+X^{u-1}
1+X+...+Xu−1和
1
+
X
+
.
.
.
+
X
v
−
1
1+X+...+X^{v-1}
1+X+...+Xv−1也互素(因为复数域上它们的根分别是
u
u
u次单位根和
v
v
v次单位根除去
1
1
1,当
u
u
u和
v
v
v互素的时候,它们在
C
[
X
]
\mathbb{C}[X]
C[X]上是互素的,在
Z
[
X
]
\mathbb{Z}[X]
Z[X]上更互素)所以有
(
1
+
X
+
.
.
.
+
X
u
−
1
)
∣
(
s
0
+
s
1
X
+
.
.
.
+
s
u
−
1
X
u
−
1
)
(1+X+...+X^{u-1}) | (s_0+s_1X+...+s_{u-1}X^{u-1})
(1+X+...+Xu−1)∣(s0+s1X+...+su−1Xu−1)而这两个多项式次数相等,所以只能相差一个常数,这个常数只能是
s
0
s_0
s0,所以得到
s
0
=
s
1
=
.
.
.
=
s
u
−
1
s_0=s_1=...=s_{u-1}
s0=s1=...=su−1,同理得
t
0
=
t
1
=
.
.
.
=
t
v
−
1
t_0=t_1=...=t_{v-1}
t0=t1=...=tv−1。又
s
0
=
t
0
s_0=t_0
s0=t0,所以就有:
s
0
=
s
1
=
.
.
.
=
s
u
−
1
=
t
0
=
t
1
=
.
.
.
=
t
v
−
1
s_0=s_1=...=s_{u-1}=t_0=t_1=...=t_{v-1}
s0=s1=...=su−1=t0=t1=...=tv−1也就是说
s
0
s_0
s0就是最大公约串,结论成立。
代码如下:
class Solution {
public:
string gcdOfStrings(string s1, string s2) {
if (s1 + s2 != s2 + s1) return "";
return s1.substr(0, gcd(s1.size(), s2.size()));
}
int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
};
时间复杂度 O ( l s + l t ) O(l_s+l_t) O(ls+lt),空间 O ( 1 ) O(1) O(1)。
注解:
字符串哈希来证明字符串的结论,可以将字符串问题转化为代数问题,问题的描述和解决都会更加清楚。