Skip to content

Commit 0d475c1

Browse files
authored
Merge pull request Dijkstra-Edu#95 from AmanDeol7/aman/sol-3
Solution #1392 - Aman Deol - 02/09/2025
2 parents 6b5bfcc + 0fcfe54 commit 0d475c1

File tree

4 files changed

+227
-0
lines changed

4 files changed

+227
-0
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Longest Happy Prefix
2+
3+
**Difficulty:** Hard
4+
**Category:** String, KMP, Rolling Hash
5+
**Leetcode Link:** [Problem Link](https://leetcode.com/problems/longest-happy-prefix/)
6+
7+
---
8+
9+
## 📝 Introduction
10+
11+
We are given a string `s`.
12+
We need to find the **longest prefix** which is also a **suffix** (excluding the entire string itself).
13+
This is sometimes referred to as a **happy prefix**.
14+
15+
Example:
16+
s = "level"
17+
Happy prefix = "l" (prefix "l", suffix "l")
18+
19+
20+
If no happy prefix exists, return the empty string `""`.
21+
22+
---
23+
24+
## 💡 Approach & Key Insights
25+
26+
1. **Brute Force (Naive)**
27+
- Try all prefix lengths and check if equal to suffix.
28+
- Very slow: O(n²).
29+
30+
2. **KMP Prefix Function (Optimal)**
31+
- Use the prefix-function (a.k.a. failure function in KMP).
32+
- It gives us the longest border of the string (prefix = suffix).
33+
- Time: O(n), Space: O(n).
34+
35+
3. **Rolling Hash (Alternative)**
36+
- Maintain hash of prefix and suffix.
37+
- Compare hashes for each possible length.
38+
- Time: O(n), Space: O(1).
39+
40+
👉 In interviews, **KMP prefix-function** is the cleanest and most accepted solution.
41+
42+
---
43+
44+
## 🛠️ Breakdown of Approaches
45+
46+
### 1️⃣ Brute Force
47+
- Generate all prefixes and check with suffix.
48+
- Time: O(n²).
49+
50+
### 2️⃣ KMP Prefix Function
51+
- Compute LPS array (`lps[i]` = longest border for substring `s[0..i]`).
52+
- Final value gives the happy prefix length.
53+
- Time: O(n).
54+
55+
### 3️⃣ Rolling Hash
56+
- Compute rolling hash from left and right.
57+
- Compare prefix-hash and suffix-hash at each step.
58+
- Time: O(n).
59+
60+
---
61+
62+
## 📊 Complexity Analysis
63+
64+
| Approach | Time Complexity | Space Complexity |
65+
|-----------------|-----------------|------------------|
66+
| Brute Force | O(n²) | O(1) |
67+
| KMP (Optimal) | O(n) | O(n) |
68+
| Rolling Hash | O(n) | O(1) |
69+
70+
---
71+
72+
## 📌 Example Walkthroughs
73+
74+
Example 1:
75+
Input: s = "level"
76+
Prefixes: ["l", "le", "lev", "leve"]
77+
Suffixes: ["evel", "vel", "el", "l"]
78+
Longest common = "l"
79+
80+
Output: "l"
81+
82+
Example 2:
83+
84+
---
85+
86+
## 🔗 Additional Resources
87+
- [Prefix Function Explanation (CP-Algorithms)](https://cp-algorithms.com/string/prefix-function.html)
88+
89+
---
90+
91+
Author: Aman Deol
92+
Date: 02/09/2025
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#include <bits/stdc++.h>
2+
using namespace std;
3+
4+
// Approach 1: Brute Force
5+
class SolutionBruteForce {
6+
public:
7+
string longestPrefix(string s) {
8+
int n = s.size();
9+
for (int i = n-1; i > 0; i--) {
10+
if (s.substr(0,i) == s.substr(n-i,i)) {
11+
return s.substr(0,i);
12+
}
13+
}
14+
return "";
15+
}
16+
};
17+
18+
// Approach 2: KMP Prefix Function
19+
class SolutionKMP {
20+
public:
21+
string longestPrefix(string s) {
22+
int n = s.size();
23+
vector<int> lps(n, 0);
24+
int j = 0;
25+
for (int i = 1; i < n; i++) {
26+
while (j > 0 && s[i] != s[j]) j = lps[j-1];
27+
if (s[i] == s[j]) lps[i] = ++j;
28+
}
29+
return s.substr(0, lps[n-1]);
30+
}
31+
};
32+
33+
// Approach 3: Rolling Hash
34+
class SolutionRollingHash {
35+
public:
36+
string longestPrefix(string s) {
37+
int n = s.size();
38+
long long mod = 1e9+7, base = 31;
39+
long long prefix = 0, suffix = 0, power = 1;
40+
int res = 0;
41+
for (int i = 0; i < n-1; i++) {
42+
prefix = (prefix * base + (s[i]-'a'+1)) % mod;
43+
suffix = (suffix + (s[n-1-i]-'a'+1) * power) % mod;
44+
power = (power * base) % mod;
45+
if (prefix == suffix) res = i+1;
46+
}
47+
return s.substr(0, res);
48+
}
49+
};
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Approach 1: Brute Force
2+
class SolutionBruteForce {
3+
public String longestPrefix(String s) {
4+
int n = s.length();
5+
for (int i = n-1; i > 0; i--) {
6+
if (s.substring(0,i).equals(s.substring(n-i))) {
7+
return s.substring(0,i);
8+
}
9+
}
10+
return "";
11+
}
12+
}
13+
14+
// Approach 2: KMP Prefix Function
15+
class SolutionKMP {
16+
public String longestPrefix(String s) {
17+
int n = s.length();
18+
int[] lps = new int[n];
19+
int j = 0;
20+
for (int i = 1; i < n; i++) {
21+
while (j > 0 && s.charAt(i) != s.charAt(j)) {
22+
j = lps[j-1];
23+
}
24+
if (s.charAt(i) == s.charAt(j)) {
25+
lps[i] = ++j;
26+
}
27+
}
28+
return s.substring(0, lps[n-1]);
29+
}
30+
}
31+
32+
// Approach 3: Rolling Hash
33+
class SolutionRollingHash {
34+
public String longestPrefix(String s) {
35+
int n = s.length();
36+
long mod = 1000000007, base = 31;
37+
long prefix = 0, suffix = 0, power = 1;
38+
int res = 0;
39+
for (int i = 0; i < n-1; i++) {
40+
prefix = (prefix * base + (s.charAt(i)-'a'+1)) % mod;
41+
suffix = (suffix + (s.charAt(n-1-i)-'a'+1) * power) % mod;
42+
power = (power * base) % mod;
43+
if (prefix == suffix) res = i+1;
44+
}
45+
return s.substring(0, res);
46+
}
47+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Approach 1: Brute Force
2+
class SolutionBruteForce:
3+
def longestPrefix(self, s: str) -> str:
4+
for i in range(len(s)-1, 0, -1):
5+
if s[:i] == s[-i:]:
6+
return s[:i]
7+
return ""
8+
9+
10+
# Approach 2: KMP Prefix Function
11+
class SolutionKMP:
12+
def longestPrefix(self, s: str) -> str:
13+
n = len(s)
14+
lps = [0] * n
15+
j = 0
16+
for i in range(1, n):
17+
while j > 0 and s[i] != s[j]:
18+
j = lps[j-1]
19+
if s[i] == s[j]:
20+
j += 1
21+
lps[i] = j
22+
return s[:lps[-1]]
23+
24+
25+
# Approach 3: Rolling Hash
26+
class SolutionRollingHash:
27+
def longestPrefix(self, s: str) -> str:
28+
n = len(s)
29+
mod, base = 10**9 + 7, 31
30+
prefix_hash, suffix_hash = 0, 0
31+
power = 1
32+
res = 0
33+
for i in range(n-1):
34+
prefix_hash = (prefix_hash * base + ord(s[i]) - ord('a') + 1) % mod
35+
suffix_hash = (suffix_hash + (ord(s[n-1-i]) - ord('a') + 1) * power) % mod
36+
power = (power * base) % mod
37+
if prefix_hash == suffix_hash:
38+
res = i+1
39+
return s[:res]

0 commit comments

Comments
 (0)