旋转字符串 | LeetCode-796 | 模拟 | KMP | 字符串匹配

🙋大家好!我是毛毛张!
🌈个人首页: 神马都会亿点点的毛毛张
🕹️KMP算法练习题

LeetCode链接:796. 旋转字符串

1.题目描述🍑

给定两个字符串, sgoal。如果在若干次旋转操作之后,s 能变成 goal ,那么返回 true

s旋转操作 就是将 s 最左边的字符移动到最右边。

  • 例如, 若 s = 'abcde',在旋转一次之后结果就是'bcdea'

示例 1:

输入: s = "abcde", goal = "cdeab"
输出: true

示例 2:

输入: s = "abcde", goal = "abced"
输出: false

提示:

  • 1 <= s.length, goal.length <= 100
  • sgoal 由小写英文字母组成

2.题解🫐

2.1 暴力解法🫒

class Solution {
    public boolean rotateString(String s, String goal) {
        // 检查两个字符串的长度,如果长度不同,返回 false
        if (s.length() != goal.length()) return false;
        
        char[] chs = s.toCharArray();// 将字符串 s 转换为字符数组
        char[] cht = goal.toCharArray();// 将目标字符串 goal 转换为字符数组
        
        // 遍历每一个可能的旋转位置
        for (int i = 0; i < chs.length; i++) {
            // 保存当前字符,以便进行旋转
            char temp = chs[0];
            // 将字符数组向左旋转一位
            for (int j = 0; j < cht.length - 1; j++) {
                chs[j] = chs[j + 1];
            }
            // 将保存的字符放到数组末尾
            chs[chs.length - 1] = temp;
            // 如果当前旋转后的字符数组等于目标字符数组,返回 true
            if (Arrays.equals(chs, cht)) return true;
        }
        
        return false;// 如果没有找到任何匹配,返回 false
    }
}

2.2 模拟🥭

  • 上面我们是通过将字符串转换成字符数组,然后实际进行旋转,当时实际上并不需要,我们可以通过取模的方式来模拟旋转字符串,这种方法称为模拟
  • 首先,如果 s s s g o a l goal goal的长度不一样,那么无论怎么旋转, s s s都不能得到 g o a l goal goal,返回 f a l s e false false。在长度一样(都为 n)的前提下,假设 s s s旋转 i i i位,则与 g o a l goal goal中的某一位字符$ goal[j] 对应的原 对应的原 对应的原s$中的字符应该为 s [ ( i + j )   m o d   n ] s[(i+j)\ mod\ n] s[(i+j) mod n]。在固定 i i i的情况下,遍历所有 j j j,若对应字符都相同,则返回 t r u e true true。否则,继续遍历其他候选的 i i i。若所有的 i i i都不能使 s s s变成 g o a l goal goal,则返回 f a l s e false false
class Solution {
    public boolean rotateString(String s, String goal) {
        // 检查两个字符串的长度,如果不相等则返回 false
        if (s.length() != goal.length()) return false;

        int len = s.length(); // 获取字符串 s 的长度
        // 遍历字符串 s 的每一个字符作为旋转的起始位置
        for (int i = 0; i < len; i++) {
            int j; // 初始化目标字符串 goal 的索引
            // 遍历目标字符串 goal
            for (j = 0; j < len; j++) {
                // 通过模运算计算旋转后的字符在字符串 s 中的位置,并进行比较
                if (s.charAt((i + j) % len) != goal.charAt(j)) break; // 如果不匹配,跳出内层循环
            }
            // 如果 j 达到目标字符串的长度,表示完全匹配,返回 true
            if (j == len) return true;
        }
        // 如果没有找到匹配,返回 false
        return false;
    }
}

2.3 字符串匹配 - 移动匹配🥑

  • 首先,如果 sgoal 的长度不一样,那么无论怎么旋转,s 都不能得到 goal,返回 false。字符串 s+s 包含了所有 s 可以通过旋转操作得到的字符串,只需要检查 goal 是否为 s+s 的子字符串即可。

2.3.1 内置函数🥥

class Solution {
    public boolean rotateString(String s, String goal) {
        return s.length() == goal.length() && (s+s).contains(goal);
    }
}

2.3.2 KMP🍊

class Solution {
    public boolean rotateString(String s, String goal) {
        // 检查字符串的长度,如果不相等则返回 false
        if (s.length() != goal.length()) return false;

        // 将字符串 s 进行拼接,以便于处理旋转情况
        s = s + s;
        // 生成目标字符串 goal 的前缀函数数组
        int[] next = getNext(goal); 

        int j = 0; // 匹配指针,用于遍历目标字符串 goal
        // 遍历拼接后的字符串 s
        for (int i = 0; i < s.length(); i++) {
            // 当前字符不匹配时,根据前缀函数回退 j
            while (j > 0 && s.charAt(i) != goal.charAt(j)) j = next[j - 1];
            if (s.charAt(i) == goal.charAt(j)) j++;// 如果字符匹配,增加 j
            // 如果 j 达到目标字符串的长度,表示完全匹配,返回 true
            if (j == goal.length()) return true;
        }

        // 如果没有找到匹配,返回 false
        return false;
    }

    // 生成目标字符串的前缀函数数组
    public int[] getNext(String s) {
        int n = s.length();
        int[] next = new int[n]; // 存储前缀函数的数组
        int j = 0; // 前缀指针
        next[0] = 0; // 第一个字符的前缀长度为0

        // 遍历字符串,生成前缀函数数组
        for (int i = 1; i < n; i++) {
            // 当前字符不匹配时,回退前缀指针 j
            while (j > 0 && s.charAt(i) != s.charAt(j)) j = next[j - 1];
            if (s.charAt(i) == s.charAt(j)) j++;// 如果字符匹配,前缀长度增加
            next[i] = j;// 记录前缀长度到 next 数组
        }
        return next; // 返回前缀函数数组
    }
}


都看到这了,不妨一键三连再走吧!

🌈欢迎和毛毛张一起探讨和交流!
联系方式参见个人主页:
神马都会亿点点的毛毛张

  • 11
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
KMP算法是一种字符串匹配算法,用于在一个文本串S内查找一个模式串P的出现位置。它的时间复杂度为O(n+m),其中n为文本串的长度,m为模式串的长度。 KMP算法的核心思想是利用已知信息来避免不必要的字符比较。具体来说,它维护一个next数组,其中next[i]表示当第i个字符匹配失败时,下一次匹配应该从模式串的第next[i]个字符开始。 我们可以通过一个简单的例子来理解KMP算法的思想。假设文本串为S="ababababca",模式串为P="abababca",我们想要在S中查找P的出现位置。 首先,我们可以将P的每个前缀和后缀进行比较,得到next数组: | i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | --- | - | - | - | - | - | - | - | - | | P | a | b | a | b | a | b | c | a | | next| 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 | 接下来,我们从S的第一个字符开始匹配P。当S的第七个字符和P的第七个字符匹配失败时,我们可以利用next[6]=4,将P向右移动4个字符,使得P的第五个字符与S的第七个字符对齐。此时,我们可以发现P的前五个字符和S的前五个字符已经匹配成功了。因此,我们可以继续从S的第六个字符开始匹配P。 当S的第十个字符和P的第八个字符匹配失败时,我们可以利用next[7]=1,将P向右移动一个字符,使得P的第一个字符和S的第十个字符对齐。此时,我们可以发现P的前一个字符和S的第十个字符已经匹配成功了。因此,我们可以继续从S的第十一个字符开始匹配P。 最终,我们可以发现P出现在S的第二个位置。 下面是KMP算法的C++代码实现:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

神马都会亿点点的毛毛张

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值