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.