Minimum Time to Revert Word to Initial State II — Solution
Minimum Time to Revert Word to Initial State II solution explained: step-by-step approach, complexity and code in Python, Java, C++ and JavaScript.
Minimum Time to Revert Word to Initial State II is a hard coding problem on strings, rolling hash and string matching, asked in Google interviews.
- Difficulty: Hard
- Topics: Strings, Rolling Hash, String Matching
- Asked at: Google
- Time limit: 2 s
- Memory limit: 256 MB
- Languages: JavaScript, TypeScript, Python, Java, C++, C, C#, Go, Kotlin, Swift, Rust, PHP and Ruby
Problem statement
You are given a string word and an integer k.
Every second you must do both of the following:
- remove the first
kcharacters ofword, and - append any
kcharacters of your choice to the end ofword.
Return the minimum time greater than zero after which word can be back in its initial state.
This is the large version of the problem: word can hold up to a million characters, so checking each candidate time by comparing strings is too slow.
Example 1
Input: word = "abacaba", k = 3
Output: 2
Explanation: After 2 seconds only the final "a" of the original survives at the front; it is a prefix of "abacaba", so appending "bacaba" restores the word.
Example 2
Input: word = "abababab", k = 3
Output: 2
Explanation: After 1 second "babab" survives, which is not a prefix. After 2 seconds "ab" survives, which is.
Example 3
Input: word = "abcbabcd", k = 2
Output: 4
Explanation: No surviving suffix is ever a prefix, so all 8 characters must be replaced: 4 seconds.
Constraints
1 <= word.length <= 10^61 <= k <= word.lengthword consists only of lowercase English letters
Hints for Minimum Time to Revert Word to Initial State II
Hint 1
After
tseconds the word can be the original again exactly when the suffixword[t*k..]is also a prefix ofword(or nothing original is left).Hint 2
So you need, for many positions
i, to know whether the suffix starting atimatches the prefix of the same length — the length of the longest common prefix ofwordandword[i..].Hint 3
The Z-function gives that length for every
iin linear time:z[i] == n - imeans the whole suffix is a prefix.
How to solve Minimum Time to Revert Word to Initial State II
The question at time t is whether word[t*k..] is a prefix of word. That is exactly z[t*k] == n - t*k, where z[i] is the longest common prefix of word and its suffix at i — and the Z-function computes all of them in O(n).
Approach
- Compute the Z-array: keep the rightmost window
[l, r)known to match the prefix. For eachi, startz[i]atmin(r - i, z[i - l])wheni < r, extend it by direct comparison, and move the window if it now reaches further right. - For
t = 1, 2, …: ift * k >= nreturnt; ifz[t*k] == n - t*kreturnt.
Why it works
After t seconds the word is word[t*k..] followed by t*k free characters, which can equal the original iff that suffix matches the original's prefix. z[i] == n - i states precisely that the suffix at i matches the prefix along its whole length. Each character extension in the Z loop pushes r right, so the total work is linear.
Complexity
- Time —
O(n) - Space —
O(n)
Pitfalls
- Comparing
word[i..]with the prefix directly for every candidatetisO(n^2 / k)— fine for the small version, too slow here whenkis 1. - If no candidate works the answer is
ceil(n / k), reached whent * k >= n. z[0]is never consulted (tstarts at 1), so its convention does not matter.
Reference solution
Python
def minimumTimeToInitialState(word: str, k: int) -> int:
n = len(word)
z = [0] * n
l = r = 0
for i in range(1, n):
if i < r:
z[i] = min(r - i, z[i - l])
while i + z[i] < n and word[z[i]] == word[i + z[i]]:
z[i] += 1
if i + z[i] > r:
l, r = i, i + z[i]
t = 1
while t * k < n:
if z[t * k] == n - t * k:
return t
t += 1
return tJava
public static int minimumTimeToInitialState(String word, int k) {
int n = word.length();
int[] z = new int[n];
int l = 0, r = 0;
for (int i = 1; i < n; i++) {
if (i < r) z[i] = Math.min(r - i, z[i - l]);
while (i + z[i] < n && word.charAt(z[i]) == word.charAt(i + z[i])) z[i]++;
if (i + z[i] > r) { l = i; r = i + z[i]; }
}
int t = 1;
while ((long) t * k < n) {
if (z[t * k] == n - t * k) return t;
t++;
}
return t;
}C++
int minimumTimeToInitialState(string word, int k) {
int n = word.size();
vector<int> z(n, 0);
int l = 0, r = 0;
for (int i = 1; i < n; i++) {
if (i < r) z[i] = min(r - i, z[i - l]);
while (i + z[i] < n && word[z[i]] == word[i + z[i]]) z[i]++;
if (i + z[i] > r) { l = i; r = i + z[i]; }
}
int t = 1;
while ((long long) t * k < n) {
if (z[t * k] == n - t * k) return t;
t++;
}
return t;
}JavaScript
var minimumTimeToInitialState = function(word, k) {
var n = word.length;
var z = new Array(n).fill(0);
var l = 0, r = 0;
for (var i = 1; i < n; i++) {
if (i < r) z[i] = Math.min(r - i, z[i - l]);
while (i + z[i] < n && word.charCodeAt(z[i]) === word.charCodeAt(i + z[i])) z[i]++;
if (i + z[i] > r) { l = i; r = i + z[i]; }
}
var t = 1;
while (t * k < n) {
if (z[t * k] === n - t * k) return t;
t++;
}
return t;
};Also on the editorial tab: C, C#, Go, Kotlin, PHP, Ruby, Rust, Swift, TypeScript.
All 515 strings problems · the whole catalogue
Learn the technique: Strings