FFT continued
POLYNOMIAL MULTIPLICATION
Input:
P(x) = sum_{i=0}^{n/2-1} a_i x^i
Q(x) = sum_{i=0}^{n/2-1} b_i x^i
Output:
PQ(x) = sum_{i=0}^{n-1} c_i x^i
where c_i = sum_{k=0}^i a_k b_{i-k}
Step 1: Convert P,Q to "point value" representation
P(x_0), P(x_1) ... P(x_{n-1})
Q(x_0), Q(x_1) ... Q(x_{n-1})
[Last time: showed these can be computed in n log n time with divide
and conquer, when x_i are the roots of unity w^i.]
Step 2:
PQ(x_i) = P(x_i) * Q(x_i) [linear time total]
Step 3:
Convert back to coefficient representation [today!]
----
Key claim: Step 3 is the same as Step 1.
Observation: We can think of Step 1 as a fast matrix vector
multiplication (for some particular matrix and vector).
In particular, we are computing
Ax = b, with
x = (a_0, ..., a_{n-1}), the coefficients of P, followed by n/2 zeros
A_{i,j} = w^{ij}
b = (P(w^0), P(w^1), ..., P(w^{n-1}))
So note that A takes coefficients to point values. So to go from point
values to coefficients, we just need to multiply by A^{-1}.
Of course, A^{-1} could be arbitrarily nasty. Turns out, it is not! In
fact A^{-1} is simply A*1/n, with w replaced by w^{-1}. Namely,
A_{i,j} = w^{-ij}/n
To verify this, we can just check.
The i'th row of A times the j'th row of A^{-1} is 1/n * sum_{k=0}^{n-1} w^{(i-j)k}.
If i=j, then w^{(i-j)k} = 1 for each k.
If i!=j, proof by picture -- we're summing a bunch of rotationally symmetric vectors.
And then lastly, multiplying by A^{-1} happens in exactly the same way
multiplying by A did in Step 1, so O(n log n)!
----
Application:
Input:
string A = a1..an of length n over some alphabet E = {1, 2, .., S}
string B = b1..bm of length m over E \cup {*}
(so B has wildcards that match anything in E)
Output:
all matches of B within A
Algorithm (no wildcards):
pick r_j uniformly at random from 1..N for all j = 1..m
N is a parameter, think of it as say 100n
compute t = sum_{j=1}^m r_j*b_j (could take mod some prime, if you'd like)
for i = 1,2..,n-m-1:
report i iff sum_{j=1}^m r_j*b_{i+j-1}
So: no false negatives. Some false positives, dependent on N.
Algorithm, with wildcards:
Set b_j=0 for each wildcard.
Now, if you look at what exactly needs to be computed, it looks very
similar to the polynomial multiplication computation. To make it the
same, one of the polynomials needs to be written in reverse.