Skip to main content

Unit - 7

TITLE

String Matching & NP-Completeness​

1. Introduction to String Matching​

1.1 Problem Definition and Formal Model​

The string-matching problem formalizes the task of locating all valid occurrences of a given pattern string within a larger body of text.

  • Text: Modeled as an array T[1…n]T[1 \dots n] of length nn.
  • Pattern: Modeled as an array P[1…m]P[1 \dots m] of length mm, where m≤nm \le n.
  • Both TT and PP are composed of character elements chosen from a finite alphabet Σ\Sigma.
  • Valid Shift: We say that pattern PP occurs with shift ss in text TT if: 0≤s≤n−mandT[s+1…s+m]=P[1…m]0 \le s \le n - m \quad \text{and} \quad T[s + 1 \dots s + m] = P[1 \dots m] If PP occurs with shift ss in TT, then ss is called a valid shift; otherwise, it is an invalid shift.

1.2 Applications in Modern Computing​

  • Text Editing & Word Processors: Find and replace tools, syntax highlighting, spelling correctors.
  • Search Engines & Information Retrieval: Indexing and fast retrieval of queries across internet documents.
  • Bioinformatics & Computational Genomics: Locating specific nucleotide (DNA/RNA: {A,C,G,T}\{A, C, G, T\}) or amino acid sequence motifs.
  • Cybersecurity & Network Intrusion Detection: Inspecting packet payloads against known malicious signatures (e.g., Snort rules).

2. Naive String Matching Algorithm​

2.1 Working Principle and Shift Exploration​

The Naive String Matching algorithm finds all valid shifts by systematically testing every possible shift s∈{0,1,…,n−m}s \in \{0, 1, \dots, n - m\}:

  • For each shift ss, it performs a character-by-character comparison between P[1…m]P[1 \dots m] and the text window T[s+1…s+m]T[s + 1 \dots s + m].
  • As soon as a mismatch is detected, it shifts the pattern forward by exactly 1 position (s←s+1s \leftarrow s + 1) and restarts comparison from the first character of PP.

2.2 Algorithm Pseudocode​

💡 The Big Idea in Plain English​

Think of holding a small cardboard cutout with your search word (the pattern PP) and sliding it along a printed sentence (the text TT): You start at the beginning (s=0s = 0). You check character by character. If every letter matches, you found a match! Then you simply slide the cutout 1 character to the right (s=s+1s = s + 1) and check again. Repeat until you reach the end of the sentence.

👣 3 Easy Steps to Remember​

  1. Slide 1 Step at a Time: Test every starting shift ss from 00 to n−mn - m.
  2. Compare Window: Check if pattern P[1…m]P[1 \dots m] equals text slice T[s+1…s+m]T[s+1 \dots s+m].
  3. Report Match: If all characters match, announce valid shift ss.

🧠 Memory Trick: Slide window by 1 →\to Compare all mm letters →\to Repeat! Zero preprocessing, worst-case time O((n−m+1)m)O((n - m + 1)m), extra space O(1)O(1).

2.3 Step-by-Step Trace Example​

Given:

  • Text: T="acaabc"T = \text{"acaabc"} (n=6n = 6)
  • Pattern: P="aab"P = \text{"aab"} (m=3m = 3)
  • Possible shifts: s∈{0,1,2,3}s \in \{0, 1, 2, 3\} (n−m=6−3=3n - m = 6 - 3 = 3)
Shift (ss)Text Window T[s+1…s+m]T[s+1 \dots s+m]Pattern P[1…m]P[1 \dots m]Character ComparisonsOutcome
s=0s = 0a c aa a bT[1]==P[1]T[1] == P[1] ('a' == 'a'), but T[2]≠P[2]T[2] \ne P[2] ('c' ≠\ne 'a')Mismatch at index 2
s=1s = 1c a aa a bT[2]≠P[1]T[2] \ne P[1] ('c' ≠\ne 'a')Mismatch at index 1
s=2s = 2a a ba a bT[3]==P[1]T[3]==P[1], T[4]==P[2]T[4]==P[2], T[5]==P[3]T[5]==P[3] ('a'=='a', 'a'=='a', 'b'=='b')Valid Match! (s=2s = 2)
s=3s = 3a b ca a bT[4]==P[1]T[4] == P[1] ('a' == 'a'), but T[5]≠P[2]T[5] \ne P[2] ('b' ≠\ne 'a')Mismatch at index 2

2.4 Complexity Analysis​

  • Preprocessing Time: None (O(0)O(0)).
  • Worst-Case Time Complexity: O((n−m+1)m)O((n - m + 1)m)
    • Occurs when text and pattern consist of repeating identical characters with a mismatch at the end (e.g., T="aaaaaaa"T = \text{"aaaaaaa"}, P="aaab"P = \text{"aaab"}).
  • Best-Case Time Complexity: O(n)O(n) (when the very first character mismatches at every shift).
  • Space Complexity: O(1)O(1) auxiliary space.

3. Rabin-Karp Algorithm​

3.1 Hash-Based String Matching Concept​

The Rabin-Karp algorithm speeds up string matching by comparing numerical hash values of the pattern and text windows before performing character-by-character verification:

  1. Treat characters as digits in a radix-dd positional notation (where d=∣Σ∣d = |\Sigma|).
  2. Compute the hash value pp for the pattern P[1…m]P[1 \dots m].
  3. Compute the hash value tst_s for each sliding text window T[s+1…s+m]T[s + 1 \dots s + m].
  4. If p==tsp == t_s, perform a character verification to guard against spurious hits (hash collisions).

3.2 Radix Notation and Rolling Hash Formula​

To prevent large integers from causing arithmetic overflow, calculations are performed modulo a large prime qq:

  • Pattern hash: p=(P[1]⋅dm−1+P[2]⋅dm−2+⋯+P[m]) mod qp = (P[1] \cdot d^{m-1} + P[2] \cdot d^{m-2} + \dots + P[m]) \bmod q
  • Initial text window (s=0s = 0): t0=(T[1]⋅dm−1+T[2]⋅dm−2+⋯+T[m]) mod qt_0 = (T[1] \cdot d^{m-1} + T[2] \cdot d^{m-2} + \dots + T[m]) \bmod q
  • Precomputed constant: h=dm−1 mod qh = d^{m-1} \bmod q
  • Rolling Hash Formula (compute ts+1t_{s+1} from tst_s in O(1)O(1) time): ts+1=(d⋅(ts−T[s+1]⋅h)+T[s+m+1]) mod qt_{s+1} = \left( d \cdot (t_s - T[s + 1] \cdot h) + T[s + m + 1] \right) \bmod q

3.3 Spurious Hits vs. Valid Matches​

  • Valid Match: When ts≡p(modq)t_s \equiv p \pmod q AND T[s+1…s+m]==P[1…m]T[s + 1 \dots s + m] == P[1 \dots m].
  • Spurious Hit: When ts≡p(modq)t_s \equiv p \pmod q, but T[s+1…s+m]≠P[1…m]T[s + 1 \dots s + m] \ne P[1 \dots m] (a hash collision due to the modulo operation).

3.4 Algorithm Pseudocode​

💡 The Big Idea in Plain English​

Instead of comparing every single character at every step, Rabin-Karp treats strings like numbers and compares numeric fingerprints (hashes)! Imagine looking for "26" inside "31415926535":

  1. Compute the hash fingerprint of "26" (say, 26 mod 11=426 \bmod 11 = 4).
  2. Slide across the text: as the window moves, compute each 2-digit window's hash in O(1)O(1) time using a rolling hash (subtract the old left digit, add the new right digit).
  3. If the window's hash matches 44, you check the characters to make sure it's a genuine match and not a false alarm (spurious hit)!

👣 3 Easy Steps to Remember​

  1. Hash the Pattern & First Window: Calculate hash pp for pattern PP, and hash t0t_0 for the first mm characters of TT (modulo prime qq).
  2. Check Hash Match:
    • If p==tsp == t_s: do a character-by-character check. If all letters match, report shift ss!
  3. Roll to Next Window in O(1)O(1):
    • Drop the old outgoing left character, multiply by base dd, add the new incoming right character, and apply modulo qq.
    • Repeat until the end of text!

🧠 Memory Trick: Fingerprint comparison →\to Hashes match? Verify letters! →\to Slide window: roll hash in O(1)O(1)! Average time is O(n+m)O(n + m), auxiliary space is O(1)O(1).

3.5 Step-by-Step Trace Example​

Given:

  • Text: T="31415926535"T = \text{"31415926535"} (n=11n = 11)
  • Pattern: P="26"P = \text{"26"} (m=2m = 2)
  • Base d=10d = 10, Prime q=11q = 11
  • h=102−1 mod 11=10h = 10^{2-1} \bmod 11 = 10
  • Pattern hash: p=26 mod 11=4p = 26 \bmod 11 = \mathbf{4}

Sliding Window Hash Calculations:

Shift (ss)Text SubstringValueValue  mod 11\bmod 11 (tst_s)Status (ts==pt_s == p)Verification CheckResult
03 13131 mod 11=931 \bmod 11 = \mathbf{9}9≠49 \ne 4SkippedNo match
11 41414 mod 11=314 \bmod 11 = \mathbf{3}3≠43 \ne 4SkippedNo match
24 14141 mod 11=841 \bmod 11 = \mathbf{8}8≠48 \ne 4SkippedNo match
31 51515 mod 11=415 \bmod 11 = \mathbf{4}4==44 == 4"15" ≠\ne "26"Spurious Hit
45 95959 mod 11=459 \bmod 11 = \mathbf{4}4==44 == 4"59" ≠\ne "26"Spurious Hit
59 29292 mod 11=492 \bmod 11 = \mathbf{4}4==44 == 4"92" ≠\ne "26"Spurious Hit
62 62626 mod 11=426 \bmod 11 = \mathbf{4}4==44 == 4"26" ==== "26"Valid Match! (s=6s = 6)
76 56565 mod 11=1065 \bmod 11 = \mathbf{10}10≠410 \ne 4SkippedNo match
85 35353 mod 11=953 \bmod 11 = \mathbf{9}9≠49 \ne 4SkippedNo match
93 53535 mod 11=235 \bmod 11 = \mathbf{2}2≠42 \ne 4SkippedNo match

3.6 Complexity and Performance Characteristics​

  • Preprocessing Time: Θ(m)\Theta(m)
  • Worst-Case Time Complexity: Θ((n−m+1)m)\Theta((n - m + 1)m)
    • Occurs when all windows produce hash matches and require full string comparisons (e.g., T="1111111"T = \text{"1111111"}, P="111"P = \text{"111"}).
  • Average / Expected Running Time: O(n+m)O(n + m) (with suitable choice of prime q≫mq \gg m).
  • Space Complexity: O(1)O(1) auxiliary memory.

4. String Matching with Finite Automata (FA)​

4.1 5-Tuple Automaton Model​

A string-matching automaton is a deterministic finite state machine defined by the 5-tuple:

M=(Q,q0,A,Σ,δ)M = (Q, q_0, A, \Sigma, \delta)
  • Q={0,1,…,m}Q = \{0, 1, \dots, m\}: Set of states, where state qq denotes that a prefix of length qq of PP has been matched.
  • q0=0q_0 = 0: Initial state (no characters matched yet).
  • A={m}A = \{m\}: Accepting state (indicating a complete match of P[1…m]P[1 \dots m]).
  • Σ\Sigma: Finite input alphabet.
  • δ:Q×Σ→Q\delta: Q \times \Sigma \to Q: State transition function.

4.2 Suffixes and Prefix-Suffix Matching Concept​

  • Suffix Notation: ω⊐x\omega \sqsupset x indicates that string ω\omega is a suffix of string xx.
  • Prefix Function σ(x)\sigma(x): The length of the longest prefix of PP that is also a suffix of xx: σ(x)=max⁡{k:Pk⊐x}\sigma(x) = \max \{k : P_k \sqsupset x\}
  • The state transition function is defined as: δ(q,a)=σ(Pqa)\delta(q, a) = \sigma(P_q a)

4.3 Transition Function Computation (δ\delta)​

💡 The Big Idea in Plain English​

A Finite Automaton works like a board game with numbered circles (states 0…m0 \dots m): State qq means: "We have matched the first qq letters of the pattern so far." The transition function δ(q,a)\delta(q, a) is the rulebook: "If we are currently at state qq and the next character in the book is 'aa', which state should we jump to?"

  • If 'aa' is the expected next letter, we jump forward (q→q+1q \to q + 1).
  • If 'aa' is a mismatch, we don't start from scratch! We jump back to the longest prefix of the pattern that still matches what we just read.

👣 3 Easy Steps to Remember​

  1. Loop Through All States & Letters: For every state q=0…mq = 0 \dots m and every alphabet letter a∈Σa \in \Sigma.
  2. Find the Longest Common Overlap: Start at candidate length k=q+1k = q + 1 and count down until prefix P[1…k]P[1 \dots k] matches the end of string PqaP_q a.
  3. Save Next State: Set δ(q,a)=k\delta(q, a) = k.

🧠 Memory Trick: State qq = qq letters matched →\to For next char aa, jump to longest prefix that fits suffix of PqaP_q a! Preprocessing time: O(m3∣Σ∣)O(m^3 |\Sigma|).

4.4 Transition Table Walkthrough: Pattern ababaca​

Given P="ababaca"P = \text{"ababaca"} (m=7m = 7) over Σ={a,b,c}\Sigma = \{a, b, c\}:

State (qq)Match String PqP_qInput 'a'Input 'b'Input 'c'
0ϵ\epsilon1 (P1="a"P_1 = \text{"a"})00
1a1 (P1="a"P_1 = \text{"a"})2 (P2="ab"P_2 = \text{"ab"})0
2ab3 (P3="aba"P_3 = \text{"aba"})00
3aba1 (P1="a"P_1 = \text{"a"})4 (P4="abab"P_4 = \text{"abab"})0
4abab5 (P5="ababa"P_5 = \text{"ababa"})00
5ababa1 (P1="a"P_1 = \text{"a"})4 (P4="abab"P_4 = \text{"abab"})6 (P6="ababac"P_6 = \text{"ababac"})
6ababac7 (P7="ababaca"P_7 = \text{"ababaca"})00
7ababaca1 (P1="a"P_1 = \text{"a"})2 (P2="ab"P_2 = \text{"ab"})0

4.5 Finite Automaton Matcher Algorithm and Trace​

💡 The Big Idea in Plain English​

Once the transition table δ\delta has been built, scanning through text is lightning-fast: You start at State 00. You read the text character by character from left to right, and make exactly one table lookup per character: q = delta(q, text[i]) You never step backward in the text! Whenever your current state reaches mm (the length of the pattern), it means you've successfully read all characters of the pattern in order! You shout: "Match found at position i−mi - m!"

👣 3 Easy Steps to Remember​

  1. Initialize State: Set current state q=0q = 0.
  2. Scan Text Linearly:
    • For each character T[i]T[i] from i=1i = 1 to nn:
    • Look up the new state: q=δ(q,T[i])q = \delta(q, T[i]).
  3. Check for Match:
    • If q==mq == m: pattern found with shift i−mi - m!

🧠 Memory Trick: Start at 0 →\to One state transition per letter: q=δ(q,T[i])→q = \delta(q, T[i]) \to Reach state mm? Pattern matched! Strictly linear matching time: Θ(n)\Theta(n).

Trace on Text T="abababacaba"T = \text{"abababacaba"}:

Step (ii)Text Char T[i]T[i]State Transition (δ(q,T[i])\delta(q, T[i]))New State (qq)Condition (q==7q == 7)
1'a'δ(0,′a′)\delta(0, 'a')1-
2'b'δ(1,′b′)\delta(1, 'b')2-
3'a'δ(2,′a′)\delta(2, 'a')3-
4'b'δ(3,′b′)\delta(3, 'b')4-
5'a'δ(4,′a′)\delta(4, 'a')5-
6'b'δ(5,′b′)\delta(5, 'b')4(Reverts to state 4)
7'a'δ(4,′a′)\delta(4, 'a')5-
8'c'δ(5,′c′)\delta(5, 'c')6-
9'a'δ(6,′a′)\delta(6, 'a')7Match at shift i−m=9−7=2i - m = 9 - 7 = 2!
10'b'δ(7,′b′)\delta(7, 'b')2-
11'a'δ(2,′a′)\delta(2, 'a')3-

4.6 Complexity Analysis​

  • Preprocessing Time: O(m3∣Σ∣)O(m^3 |\Sigma|) (can be optimized to O(m∣Σ∣)O(m |\Sigma|)).
  • Matching Time: Θ(n)\Theta(n) (strictly linear; exactly one state transition per character).
  • Space Complexity: O(m∣Σ∣)O(m |\Sigma|) to store the transition table δ\delta.

5. Knuth-Morris-Pratt (KMP) Algorithm​

5.1 Motivation: Eliminating Text Backtracking​

The Knuth-Morris-Pratt (KMP) algorithm eliminates the need to backtrack the text pointer ii:

  • When a mismatch occurs after matching qq characters, the pattern itself contains information about where the next possible match could begin.
  • It precomputes a Prefix Function π\pi (or LPS Array - Longest Proper Prefix which is also a Suffix).

5.2 Proper Prefixes, Proper Suffixes, and the π\pi Function​

  • Proper Prefix: A prefix of string SS that is strictly shorter than SS (Pk⊏SP_k \sqsubset S).
    • For "Snape": {"S", "Sn", "Sna", "Snap"}.
  • Proper Suffix: A suffix of string SS that is strictly shorter than SS (Pk⊐SP_k \sqsupset S).
    • For "Hagrid": {"agrid", "grid", "rid", "id", "d"}.
  • Prefix Function π[q]\pi[q]: π[q]=max⁡{k:k<q and Pk⊐Pq}\pi[q] = \max \{k : k < q \text{ and } P_k \sqsupset P_q\} π[q]\pi[q] stores the length of the longest proper prefix of PP that is also a proper suffix of P[1…q]P[1 \dots q].

5.3 Prefix Function Computation Algorithm​

5.3 Prefix Function Computation Algorithm​

💡 The Big Idea in Plain English​

The secret behind KMP is the Prefix Function π\pi (also called the LPS Array — Longest Prefix which is also a Suffix). It asks a very simple question for every slice of the pattern: "Does the beginning of this slice match its end?" For example, in "ABA":

  • The prefix "A" is identical to the suffix "A" (length 1!). If you match "ABA" and the next character fails, you don't start over from scratch! You know the tail ends with "A", so you can immediately resume comparing from the second letter!

👣 3 Easy Steps to Remember​

  1. Start at Zero: Set π[1]=0\pi[1] = 0 (a 1-character string has no proper prefix). Track current matched length k=0k = 0.
  2. Loop qq from 2 to mm:
    • Mismatch? Fall back using previous knowledge: while k>0k > 0 and P[k+1]≠P[q]P[k+1] \ne P[q], jump back k=π[k]k = \pi[k].
    • Match! If P[k+1]==P[q]P[k+1] == P[q], expand the matching border: k=k+1k = k + 1.
  3. Record Length: Save the longest match length for position qq: π[q]=k\pi[q] = k.

🧠 Memory Trick: LPS Array →\to Match? k=k+1→k = k + 1 \to Mismatch? Fall back k=π[k]→k = \pi[k] \to Save π[q]=k\pi[q] = k! Preprocessing takes Θ(m)\Theta(m) time.

5.4 Step-by-Step Prefix Function Traces​

5.4.1 Example 1: ababaca​

qqSub-pattern P[1…q]P[1 \dots q]Proper PrefixesProper SuffixesLongest Commonπ[q]\pi[q]
1a∅\emptyset∅\emptysetNone0
2ababNone0
3abaa, aba, baa1
4ababa, ab, abab, ab, babab2
5ababaa, ab, aba, ababa, ba, aba, babaaba3
6ababac......None0
7ababaca......a1

π=[0,0,1,2,3,0,1]\pi = [0, 0, 1, 2, 3, 0, 1]

5.4.2 Example 2: acacabacacabacacac (Length 18)​

Index qq123456789101112131415161718
Pattern PPacacabacacabacacac
π[q]\pi[q]00123012345678910114

5.4.3 Example 3: ACACAGT​

Index qq1234567
Pattern PPACACAGT
π[q]\pi[q]0012300

5.5 KMP Matcher Algorithm and Text Search Trace​

💡 The Big Idea in Plain English​

In naive string matching, every time a mismatch happens, you yank the pointer back and re-read characters you already verified. That's a massive waste of time! KMP never moves backward in the text (ii only moves strictly forward →\to)! If you have matched qq characters and the next character clashes: You look up π[q]\pi[q] to see how many characters from the start of your pattern are already aligned with what you just read. You slide the pattern forward instantly without backtracking, and resume checking!

👣 3 Easy Steps to Remember​

  1. Precompute the Table: Compute the prefix table π=COMPUTE-PREFIX-FUNCTION(P)\pi = \text{COMPUTE-PREFIX-FUNCTION}(P). Start match count q=0q = 0.
  2. Scan Text Linearly (i=1…ni = 1 \dots n):
    • Mismatch? Slide pattern forward: while q>0q > 0 and P[q+1]≠T[i]P[q + 1] \ne T[i], fall back q=π[q]q = \pi[q].
    • Match! If P[q+1]==T[i]P[q + 1] == T[i], advance q=q+1q = q + 1.
  3. Full Pattern Matched (q==mq == m):
    • Report valid shift at position i−mi - m!
    • Reset q=π[q]q = \pi[q] to keep searching for overlapping matches.

🧠 Memory Trick: Text pointer ii never backs up! Mismatch? Fall back q=π[q]q = \pi[q]. Match? Advance q=q+1q = q + 1. Found all mm? Print shift! Strictly linear total time: Θ(n+m)\Theta(n + m).

Walkthrough:

  • Text: T="acatacgacacagt"T = \text{"acatacgacacagt"}, Pattern: P="acacagt"P = \text{"acacagt"} (π=[0,0,1,2,3,0,0]\pi = [0, 0, 1, 2, 3, 0, 0]).
  • Match reaches q=3q = 3 ("aca"), next char in text is 't' while P[4]=’c’P[4] = \text{'c'}.
  • Fallback: q←π[3]=1q \leftarrow \pi[3] = 1. It resumes comparing from P[2]P[2] against text char 't', avoiding re-examining matched prefix 'a'.
  • Shift continues smoothly until valid match is reported at shift s=7s = 7.

5.6 Complexity Analysis​

  • Preprocessing Time: Θ(m)\Theta(m) to compute π\pi.
  • Matching Time: Θ(n)\Theta(n) (inner while loop amortizes to O(n)O(n) because qq increases at most nn times).
  • Total Time Complexity: Θ(n+m)\Theta(n + m)
  • Space Complexity: Θ(m)\Theta(m) for table π\pi.

6. String Matching Algorithms (Formal Procedures)​

6.1 Naive String Matching Algorithm​

💡 The Big Idea in Plain English​

Line up the pattern against the text starting at index ss. Use an inner loop to check letter-by-letter: if character jj matches, advance to character j+1j + 1. If your inner pointer makes it all the way past the end of the pattern (j>mj > m), every single letter matched! Print shift ss. Then move the starting shift ss forward by 1 and repeat!

👣 3 Easy Steps to Remember​

  1. Slide Shift by Shift: Outer loop tests each starting position s=0…n−ms = 0 \dots n - m.
  2. Inner Character Walk: Start j=1j = 1. While characters match (T[s+j]==P[j]T[s + j] == P[j]) and j≤mj \le m, advance j=j+1j = j + 1.
  3. Verify Match: If j>mj > m, you matched the whole pattern! Output shift ss.

🧠 Memory Trick: Outer loop slides s→s \to Inner while-loop matches characters →\to If j>mj > m, pattern found!

6.2 Rabin-Karp Matching Algorithm​

💡 The Big Idea in Plain English​

This is the complete, formal procedure for the Rabin-Karp algorithm:

  1. It turns pattern PP and the first text window into numerical hash values (pp and tt).
  2. It slides the window across the text:
    • When hashes match (p==tp == t), it loops through characters to filter out false alarms (spurious hits).
    • It computes the next window's hash in O(1)O(1) time by subtracting the outgoing left digit and adding the incoming right digit (adding qq if negative).

👣 3 Easy Steps to Remember​

  1. Preprocessing (Calculate Fingerprints):
    • Calculate constant h=dm−1 mod qh = d^{m-1} \bmod q.
    • Compute initial hash pp (pattern) and tt (first window of TT).
  2. Match Check:
    • For each shift s=0…n−ms = 0 \dots n - m:
    • If p==tp == t: compare characters. If identical, print valid shift ss!
  3. Roll the Hash to Next Window:
    • t=(d×(t−T[s+1]×h)+T[s+m+1]) mod qt = (d \times (t - T[s+1] \times h) + T[s+m+1]) \bmod q.
    • If t<0t < 0, wrap around: t=t+qt = t + q.

🧠 Memory Trick: Hashes equal? Check letters! →\to Next window: roll hash in O(1)O(1) (subtract left, multiply base dd, add right)! Average time: O(n+m)O(n + m).

6.3 Knuth-Morris-Pratt (KMP) Matching Algorithm​

💡 The Big Idea in Plain English​

This is the complete, formal procedure for the Knuth-Morris-Pratt (KMP) matcher:

  1. ComputePrefixFunction(P) builds the auxiliary table π\pi (the LPS array), which figures out if the beginning of any pattern prefix matches its own tail.
  2. KMPMatcher(T, P) reads text TT strictly from left to right with pointer ii:
    • On a mismatch: It looks up π[q]\pi[q] and shifts the pattern forward without rewinding ii.
    • On a match: It advances q=q+1q = q + 1.
    • On a full match (q==mq == m): It announces the match at shift i−mi - m and uses π[q]\pi[q] to seamlessly hunt for overlapping occurrences!

👣 3 Easy Steps to Remember​

  1. Compute Table π\pi:
    • Loop qq from 22 to mm. Fall back along π\pi on mismatch (k=π[k]k = \pi[k]). Advance on match (k=k+1k = k + 1). Store π[q]=k\pi[q] = k.
  2. Scan Text Forward (i=1…ni = 1 \dots n):
    • If mismatch while q>0q > 0: fall back q=π[q]q = \pi[q].
    • If characters match: increment q=q+1q = q + 1.
  3. Record Shifts:
    • When q==mq == m: report valid shift i−mi - m, then reset q=π[q]q = \pi[q] to find overlapping matches.

🧠 Memory Trick: Text pointer ii never retreats! Mismatch? q=π[q]q = \pi[q]. Match? q=q+1q = q + 1. Reach mm? Shift reported! Total time: Θ(n+m)\Theta(n + m).

7. Asymptotic Notation & Complexity Foundations​

7.1 Performance Analysis Definitions​

Asymptotic notations describe the behavior of algorithm running time as the input size nn tends toward infinity.

7.2 Big-O (OO), Big-Ω\Omega (Ω\Omega), and Big-Θ\Theta (Θ\Theta)​

NotationFormal DefinitionMathematical MeaningNature of Bound
Big-O (OO)O(g(n))={f(n):∃c>0,n0≥0 s.t. 0≤f(n)≤c⋅g(n),∀n≥n0}O(g(n)) = \{f(n) : \exists c > 0, n_0 \ge 0 \text{ s.t. } 0 \le f(n) \le c \cdot g(n), \forall n \ge n_0\}f(n)≤c⋅g(n)f(n) \le c \cdot g(n)Asymptotic Upper Bound
Big-Ω\Omega (Ω\Omega)Ω(g(n))={f(n):∃c>0,n0≥0 s.t. 0≤c⋅g(n)≤f(n),∀n≥n0}\Omega(g(n)) = \{f(n) : \exists c > 0, n_0 \ge 0 \text{ s.t. } 0 \le c \cdot g(n) \le f(n), \forall n \ge n_0\}f(n)≥c⋅g(n)f(n) \ge c \cdot g(n)Asymptotic Lower Bound
Big-Θ\Theta (Θ\Theta)Θ(g(n))={f(n):∃c1,c2>0,n0≥0 s.t. 0≤c1g(n)≤f(n)≤c2g(n),∀n≥n0}\Theta(g(n)) = \{f(n) : \exists c_1, c_2 > 0, n_0 \ge 0 \text{ s.t. } 0 \le c_1 g(n) \le f(n) \le c_2 g(n), \forall n \ge n_0\}c1g(n)≤f(n)≤c2g(n)c_1 g(n) \le f(n) \le c_2 g(n)Asymptotically Tight Bound

7.3 Exponential Complexity and Infeasibility​

Algorithms running in O(2n)O(2^n) or O(n!)O(n!) time become computationally intractable even for moderate input sizes:

  • Example: Naive recursive calculation of Fibonacci numbers (T(n)=T(n−1)+T(n−2)≈O(1.618n)T(n) = T(n-1) + T(n-2) \approx O(1.618^n)).
  • A computer computing fib(50) recursively would require over 101410^{14} operations, stalling execution for days, whereas dynamic programming computes it in O(n)O(n) operations within microseconds.

8. Complexity Classes: P, NP, NP-Complete, and NP-Hard​

8.1 Class P (Deterministic Polynomial Time)​

  • Definition: The class of decision problems solvable in polynomial time by a deterministic Turing Machine (deterministic algorithm).
  • Running time is bounded by O(nk)O(n^k) for some fixed constant kk.
  • Examples:
    • Sorting algorithms (O(nlog⁡n)O(n \log n))
    • Minimum Spanning Trees (O(Elog⁡V)O(E \log V))
    • Shortest Path / Dijkstra (O(V2)O(V^2) or O(E+Vlog⁡V)O(E + V \log V))
    • Fractional Knapsack (O(nlog⁡n)O(n \log n))

8.2 Class NP (Non-Deterministic Polynomial Time)​

  • Definition: The class of decision problems whose candidate solution (certificate/witness) can be verified in polynomial time by a deterministic algorithm.
  • Problems in NP are verifiable quickly, even if finding the solution requires exponential time via exhaustive search.
  • Key distinction: Finding a solution vs. verifying a proposed solution.

8.3 Class NP-Complete​

A decision problem XX is NP-Complete if:

  1. X∈NPX \in NP (solutions are verifiable in polynomial time).
  2. Every problem Y∈NPY \in NP is polynomial-time reducible to XX (Y≤PXY \le_P X).
  • Significance: NP-Complete problems are the hardest problems in NP. If any single NP-Complete problem is solved in polynomial time, then P=NPP = NP.
  • No polynomial-time algorithm is known for any NP-Complete problem.
  • Examples: Circuit-SAT, 3-SAT, Vertex Cover, Hamiltonian Cycle, 0/1 Knapsack (Decision).

8.4 Class NP-Hard​

A problem XX is NP-Hard if every problem Y∈NPY \in NP can be reduced to XX in polynomial time (Y≤PXY \le_P X):

  • NP-Hard problems are at least as hard as NP-Complete problems.
  • Key distinction: NP-Hard problems do not need to be in NP (they do not need to be decision problems, nor verifiable in polynomial time).
  • Examples:
    • Traveling Salesperson Problem (Optimization)
    • Turing Halting Problem (Undecidable, hence not in NP, but NP-Hard)

8.5 P vs. NP Classification and Euler Diagram​

  • Fundamental Theorem: P⊆NPP \subseteq NP
  • If P≠NPP \ne NP: PP and NP-CompleteNP\text{-Complete} are disjoint subsets of NPNP.

9. Polynomial-Time Reductions​

9.1 Concept of Problem Reduction (A≤PBA \le_P B)​

Let AA and BB be two decision problems. A polynomial-time reduction (A≤PBA \le_P B) is a deterministic algorithm that transforms any instance α\alpha of problem AA into an instance β\beta of problem BB such that:

  1. The transformation executes in polynomial time (O(nk)O(n^k)).
  2. The outputs are identical: α is a YES-instance of A  ⟺  β is a YES-instance of B\alpha \text{ is a YES-instance of } A \iff \beta \text{ is a YES-instance of } B.

9.2 Theoretical Implications​

  • Proving Tractability ("Easiness"): If B∈PB \in P and A≤PBA \le_P B, then A∈PA \in P. We use the efficiency of BB to solve AA.
  • Proving Intractability ("Hardness"): If AA is known to be NP-Hard and A≤PBA \le_P B, then BB is also NP-Hard.

10. Classic NP Problems & Hamiltonian Cycles​

10.1 Hamiltonian Cycle Problem​

  • Instance: A connected directed or undirected graph G=(V,E)G = (V, E) with nn vertices.
  • Hamiltonian Cycle: A round-trip closed loop along nn edges that visits every vertex in VV exactly once and returns to the initial vertex.
  • Decision Formulation: "Does graph GG contain a Hamiltonian cycle?"

10.2 Polynomial Verification​

Given a proposed sequence of vertices C=⟨v1,v2,…,vn,vn+1⟩C = \langle v_1, v_2, \dots, v_n, v_{n+1} \rangle:

  1. Verify v1==vn+1v_1 == v_{n+1}.
  2. Verify all intermediate vertices v1,…,vnv_1, \dots, v_n are distinct.
  3. Verify that edge (vi,vi+1)∈E(v_i, v_{i+1}) \in E for all 1≤i≤n1 \le i \le n.
  • Total verification time: O(V)O(V) operations   ⟹  \implies Hamiltonian Cycle ∈NP\in NP.

10.3 Backtracking Procedure for Finding Hamiltonian Cycles​

  1. Define a solution vector X=(X1,X2,…,Xn)X = (X_1, X_2, \dots, X_n) where XiX_i represents the ii-th visited vertex.
  2. Initialize X[1]=1X[1] = 1 (without loss of generality, start at vertex 1).
  3. For position kk from 22 to nn:
    • Check if an edge exists from X[k−1]X[k-1] to X[k]X[k].
    • Check if X[k]X[k] has not been visited earlier.
  4. When k=nk = n, check if an edge exists from X[n]X[n] back to X[1]X[1]. If so, a valid cycle is found; otherwise, backtrack.

10.4 Example Graph G1G_1 Trace​

Valid Hamiltonian Cycles in G1G_1:

  1. 1→3→4→5→6→7→8→2→11 \to 3 \to 4 \to 5 \to 6 \to 7 \to 8 \to 2 \to 1
  2. 1→2→8→7→6→5→4→3→11 \to 2 \to 8 \to 7 \to 6 \to 5 \to 4 \to 3 \to 1

10.5 Traveling Salesperson Problem (TSP)​

  • Problem: Given a complete weighted graph G=(V,E)G = (V, E), find a Hamiltonian cycle with minimum total edge weight.
  • NP-Hard Classification: Because finding whether a cycle exists is NP-Complete, finding the minimum-weight cycle across all (n−1)!/2(n-1)! / 2 permutations is NP-Hard.

11. Decision Problems vs. Optimization Problems​

11.1 Fundamental Differences​

DimensionDecision ProblemOptimization Problem
Output TypeBinary answer: "YES" or "NO"An optimal structure or minimum/maximum value
FormulationDetermines whether a solution satisfies a bound ccFinds a solution that achieves the optimal bound
Complexity ClassBelongs directly to PP, NPNP, or NP-CompleteNP\text{-Complete}Belongs to NP-HardNP\text{-Hard} (if corresponding decision problem is NP-Complete)

11.2 Converting Optimization Problems to Decision Problems​

Every optimization problem can be cast as a decision problem by introducing a numerical threshold parameter cc:

ProblemOptimization VersionDecision Version
Minimum Spanning TreeFind a spanning tree of GG with minimum total weight.Does there exist a spanning tree of GG with total weight ≤c\le c?
Longest Common SubsequenceFind the maximum length of a common subsequence.Does there exist a common subsequence of length ≥c\ge c?
Traveling SalespersonFind a tour visiting every city once with minimum total length.Does there exist a tour of length ≤c\le c?
0/1 KnapsackMaximize ∑vixi\sum v_i x_i subject to ∑wixi≤W\sum w_i x_i \le W.Does there exist a subset of items with weight ≤W\le W and value ≥c\ge c?

11.3 Hierarchy of Computational Hardness​

  • An optimization problem is at least as difficult as its decision counterpart.
  • If the decision version is computationally hard (NP-Complete), the optimization version is automatically hard (NP-Hard).
  • An optimization problem can be solved by repeatedly querying its decision version using Binary Search over the bound cc.

12. Unit 7 Summary and Quick Revision Cheat Sheet​

12.1 String Matching Algorithms Master Table​

AlgorithmPreprocessing TimeMatching TimeTotal TimeAuxiliary SpaceKey Advantage / Mechanism
Naive00O((n−m+1)m)O((n - m + 1)m)O(nm)O(nm)O(1)O(1)Simple implementation, no extra memory
Rabin-KarpΘ(m)\Theta(m)O((n−m+1)m)O((n - m + 1)m)O(n+m)O(n + m) (avg)O(1)O(1)Modular rolling hash, ideal for multi-pattern search
Finite AutomataO(m∥Σ∥)O(m \|\Sigma\|)Θ(n)\Theta(n)Θ(n+m∥Σ∥)\Theta(n + m \|\Sigma\|)O(m∥Σ∥)O(m \|\Sigma\|)Strictly linear matching, no text backtracking
KMPΘ(m)\Theta(m)Θ(n)\Theta(n)Θ(n+m)\Theta(n + m)Θ(m)\Theta(m)Prefix function π\pi, zero text backtracking

12.2 Complexity Classes Reference Card​

ClassDefinitionVerification TimeSolution Finding TimeExample Problems
PSolvable in polynomial time deterministicallyPolynomial (O(nk)O(n^k))Polynomial (O(nk)O(n^k))Merge Sort, Dijkstra, Prim, Kruskal
NPVerifiable in polynomial timePolynomial (O(nk)O(n^k))Exponential (O(2n)O(2^n))Hamiltonian Cycle, Subset Sum, 3-SAT
NP-CompleteHardest problems in NP; all NP reduce to themPolynomial (O(nk)O(n^k))Unknown (P=?NPP \stackrel{?}{=} NP)Circuit-SAT, 3-SAT, Vertex Cover
NP-HardAt least as hard as NP-Complete; not necessarily in NPNot necessarily polyExponential / UndecidableTSP Optimization, Halting Problem

12.3 Key Formulas Cheat Sheet​

  • Rabin-Karp Rolling Hash: ts+1=(d⋅(ts−T[s+1]⋅h)+T[s+m+1]) mod q,h=dm−1 mod qt_{s+1} = \left( d \cdot (t_s - T[s + 1] \cdot h) + T[s + m + 1] \right) \bmod q, \quad h = d^{m-1} \bmod q
  • KMP Prefix Function: π[q]=max⁡{k:k<q and Pk⊐Pq}\pi[q] = \max \{k : k < q \text{ and } P_k \sqsupset P_q\}
  • Asymptotic Inclusion: f(n)=Θ(g(n))  ⟺  f(n)=O(g(n)) and f(n)=Ω(g(n))f(n) = \Theta(g(n)) \iff f(n) = O(g(n)) \text{ and } f(n) = \Omega(g(n))
  • Class Inclusion: P⊆NP,NP-Complete=NP∩NP-HardP \subseteq NP, \quad NP\text{-Complete} = NP \cap NP\text{-Hard}