算法笔记-lc-面试题 01.09. 字符串轮转(中等)

算法笔记-lc-面试题 01.09. 字符串轮转(中等)

题目

题干

字符串轮转。给定两个字符串s1和s2,请编写代码检查s2是否为s1旋转而成(比如,waterbottle是erbottlewat旋转后的字符串)。

示例

示例1:

输入:s1 = “waterbottle”, s2 = “erbottlewat”
输出:True
示例2:

输入:s1 = “aa”, s2 = “aba”
输出:False

提示:

字符串长度在[0, 100000]范围内。

说明:

你能只调用一次检查子串的方法吗?

题解

方法一:模拟

思路

首先,如果 s1和 s2的长度不一样,那么无论怎么轮转,s1都不能得到 s2 ,返回false。在长度一样(都为 n)的前提下,假设 s1轮转 i位,则与 s2中的某一位字符 s2[j] 对应的原 s1 中的字符应该为s1[(i+j)modn]。在固定 i 的情况下,遍历所有 j,若对应字符都相同,则返回true。否则,继续遍历其他候选的 i。若所有的 i 都不能使s1变成 s2
​ ,则返回 false。

class Solution {
    public boolean isFlipedString(String s1, String s2) {
        int m = s1.length(), n = s2.length();
        if (m != n) {
            return false;
        }
        if (n == 0) {
            return true;
        }
        for (int i = 0; i < n; i++) {
            boolean flag = true;
            for (int j = 0; j < n; j++) {
                if (s1.charAt((i + j) % n) != s2.charAt(j)) {
                    flag = false;
                    break;
                }
            }
            if (flag) {
                return true;
            }
        }
        return false;
    }
}

复杂度分析

时间复杂度:O(n^2),其中 n 是字符串 s1的长度。我们需要双重循环来判断。

空间复杂度:O(1)。仅使用常数空间。

方法二:搜索子字符串
思路

首先,如果 s1和 s2的长度不一样,那么无论怎么轮转,s1都不能得到 s 2 ,返回 \text{false}false。字符串 s+s 包含了所有 s1 可以通过轮转操作得到的字符串,只需要检查 s2 是否为 s+s 的子字符串即可。

class Solution {
    public boolean isFlipedString(String s1, String s2) {
        return s1.length() == s2.length() && (s1 + s1).contains(s2);
    }
}

复杂度分析

时间复杂度:O(n),其中 n 是字符串 s1的长度。KMP 算法搜索子字符串的时间复杂度为O(n),其他搜索子字符串的方法会略有差异。

空间复杂度:O(n),其中 n 是字符串 s1 的长度。KMP 算法搜索子字符串的空间复杂度为 O(n),其他搜索子字符串的方法会略有差异。

方法三:字符串哈希

若两字符串互为旋转,则「其一字符串」必然为「另一字符串拓展两倍长度后(循环子串)」的子串。

基于此,我们可以使用「字符串哈希」进行求解:先计算 s2 的字符串哈希值 t,然后构造出 s1 + s1 的哈希数组和次方数组,在两数组中检查是否存在长度为 n 的连续子段的哈希值 cur 与 t 相等。

一些细节:其他语言可能不像 Java 那样存在自然取模,可手动取模,对于自带高精度的语言若不取模会导致单次计算复杂度上升,会 TLE。

class Solution {
    static int N = 200010, P = 13131;
    static int[] h = new int[N], p = new int[N];
    public boolean isFlipedString(String s1, String s2) {
        if (s1.length() != s2.length()) return false;
        int n = s1.length();
        for (int i = 1; i <= n; i++) h[i] = h[i - 1] * P + s2.charAt(i - 1);
        int t = h[n]; // s2 hash
        s1 = s1 + s1;
        p[0] = 1;
        for (int i = 1; i <= 2 * n; i++) {
            h[i] = h[i - 1] * P + s1.charAt(i - 1);
            p[i] = p[i - 1] * P;
        }
        for (int i = 1; i + n - 1 <= 2 * n; i++) {
            int j = i + n - 1, cur = h[j] - h[i - 1] * p[j - i + 1];
            if (cur == t) return true;
        }
        return false;
    }
}

时间复杂度:O(n)
空间复杂度:O(n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值