leetcode
leetcode 2401 ~ 2450
字符串转换

字符串转换

难度:

标签:

题目描述

You are given two strings s and t of equal length n. You can perform the following operation on the string s:

  • Remove a suffix of s of length l where 0 < l < n and append it at the start of s.
    For example, let s = 'abcd' then in one operation you can remove the suffix 'cd' and append it in front of s making s = 'cdab'.

You are also given an integer k. Return the number of ways in which s can be transformed into t in exactly k operations.

Since the answer can be large, return it modulo 109 + 7.

 

Example 1:

Input: s = "abcd", t = "cdab", k = 2
Output: 2
Explanation: 
First way:
In first operation, choose suffix from index = 3, so resulting s = "dabc".
In second operation, choose suffix from index = 3, so resulting s = "cdab".

Second way:
In first operation, choose suffix from index = 1, so resulting s = "bcda".
In second operation, choose suffix from index = 1, so resulting s = "cdab".

Example 2:

Input: s = "ababab", t = "ababab", k = 1
Output: 2
Explanation: 
First way:
Choose suffix from index = 2, so resulting s = "ababab".

Second way:
Choose suffix from index = 4, so resulting s = "ababab".

 

Constraints:

  • 2 <= s.length <= 5 * 105
  • 1 <= k <= 1015
  • s.length == t.length
  • s and t consist of only lowercase English alphabets.

代码结果

运行时间: 72 ms, 内存: 0.0 MB


/*
 * 思路:
 * 使用Java Stream处理类似思路,通过k次旋转操作将s变为t的方法数量可以通过使用最大公约数(gcd)来计算。
 * 首先,找到可以使s变为t的旋转索引,然后根据这些索引来判断经过k次操作后可以变为t的情况。
 * 我们需要考虑gcd的使用,因为在k次操作中可能会重复到某个状态。
 */
import java.util.*;
import java.util.stream.*;

public class SolutionStream {
    private static final int MOD = 1000000007;

    public int numberOfWays(String s, String t, long k) {
        int n = s.length();
        List<Integer> validShifts = IntStream.range(0, n)
            .filter(i -> (s.substring(i) + s.substring(0, i)).equals(t))
            .boxed()
            .collect(Collectors.toList());

        long gcd = gcd(k, n);
        long cycles = k / gcd;
        long count = validShifts.stream()
            .filter(shift -> shift % gcd == 0)
            .count();

        return (int) ((count * cycles) % MOD);
    }

    private long gcd(long a, long b) {
        if (b == 0) return a;
        return gcd(b, a % b);
    }

    public static void main(String[] args) {
        SolutionStream solution = new SolutionStream();
        System.out.println(solution.numberOfWays("abcd", "cdab", 2)); // 输出: 2
        System.out.println(solution.numberOfWays("ababab", "ababab", 1)); // 输出: 2
    }
}

解释

方法:

本题解使用周期性和数学方法。首先,通过拼接字符串s到其自身来形成新的字符串ss,这样可以模拟任何后缀转移到前缀的操作。利用ss,算法首先查找目标字符串t在ss中的首次出现位置,如果未找到,直接返回0。如果找到,确定转移的周期性。周期性是通过在ss中查找s的起始位置1处的下一个s出现位置来确定。然后,根据周期和长度n计算在k次操作后,从首次出现位置可以转移到目标t的不同方式的数量。计算这些方式的数量涉及到使用pow函数来高效地计算大数的幂,并结合周期性和首次出现位置进行计算。最终,结合周期性出现的情况(是否从索引0开始)来计算恰好k次操作后达到目标的总方案数。

时间复杂度:

O(n + log k)

空间复杂度:

O(n)

代码细节讲解

🦆
如何确定字符串s拼接成ss后,t的首次出现位置与s的周期性之间的关系?
当字符串s拼接成ss后,可以通过ss模拟s的任意循环位移。周期性是指s的循环重复的最小单位长度。首次出现位置是指t在ss中第一次出现的位置。如果t在ss中首次出现的位置与s的周期性位置对应,这意味着t可以通过周期性位移从s的开始位置匹配。例如,如果周期性为s的长度,那么每次完整的s的位移都可能重新匹配到t,从而形成周期性行为。
🦆
在查找周期性时,为什么从ss的索引1处开始搜索s的下一个出现位置?如果s在ss中以不同的方式重叠会怎样?
从ss的索引1开始搜索s的下一个出现位置是为了找到s可能的最小周期性。即寻找s不是完整出现在ss开头时的最早出现点。如果s在ss中以不同的方式重叠,例如部分重叠,那么这种重叠会影响实际的周期性。通过从索引1开始搜索,可以确保我们找到的是除完全重叠外的最小周期。
🦆
您如何计算在k次操作后从首次出现的位置可以转移到目标t的不同方式的数量?请解释涉及的数学原理。
计算k次操作后从首次出现的位置转移到目标t的方式,涉及到使用幂运算来模拟周期性位移的影响。使用pow函数计算`(n-1)^k`,其中n是s的长度。这代表所有可能的位移组合。然后,根据首次出现的位置是0还是非0,使用不同的计算公式来确定确切的转移方式数量。如果首次出现位置为0,计算方式稍有不同,因为从索引0开始的转移和其他位置的转移在周期性上可能有所不同。
🦆
在计算方式数量时,使用的pow函数中的MOD * n是如何确定的,它的作用是什么?
在计算方式数量时,使用MOD * n主要是为了防止大数溢出并进行模运算以简化计算。MOD通常是一个大的质数,如10^9+7,用于取余保证结果的范围内有效性。乘以n是为了在取模操作中保持精度,确保在整个计算过程中不会因为过大的数字而失去精度或引发计算错误。

相关问题