This page is still under construction.

Parts of this page are still being built. What you see may change.

Better and Faster!

Interview

Time limit2sMemory limit512 MB

Summary
Compute a CRC-style bit checksum of a string after each of up to 1e5 character substitutions, fast enough that recomputing from scratch times out.
Level

Medium7 of 10

Topics
Bit manipulation, Math, Prefix sum
Solved
No attempts yet

Problem

You wake up with a pounding headache and only a hazy memory of a program your boss asked you to write. After you log in, you find the main routine you wrote yesterday:

unsigned int checksum (char str[], int len) {
    unsigned int r = 0;
    for (int k=0; k<8*len; k++) {               // iterate over the bits of str
        if ((r & (1<<31)) != 0) r = (r << 1) ^ 0x04c11db7;
                           else r = (r << 1);   // do some magic
        if (str[k/8] & 1<<(7-k%8))              // if the k-th bit of str is set,
            r ^= 1;                             // flip the last bit of r
    }
    return r;
}

You are proud of your comments, yet the "do some magic" part is still a little hard to follow. Even so, the function is named checksum, and — sure enough — it really does compute a kind of checksum of a given string, processing it one bit at a time.

Your task was to compute this checksum for a given string, and then for several slightly modified versions of it. The rest of your program looks reasonable too:

#include <stdio.h>

int main() {
    char str[1000001],c;
    int TESTS,n,changes,p;
    for (scanf ("%d", &TESTS); TESTS>0; TESTS--) {
        scanf ("%d %s", &n, str);               // read the input
        printf ("%u\n", checksum(str, n));      // checksum of the original string
        for (scanf ("%d", &changes); changes>0; changes--) {
            scanf ("%d %c", &p, &c);            // apply the change
            str[p-1] = c;
            printf ("%u\n", checksum(str, n));  // checksum of the modified string
        }
    }
}

The program is correct, but painfully slow, and you must make it run much faster. You even wrote an equivalent Java version (shown in the Hint), which somehow runs even slower.

Input

The input contains several test cases. The first line contains a positive integer Z≤20Z \le 20, the number of test cases. Then ZZ test cases follow.

Here, a character means a single lowercase letter, uppercase letter, or digit.

The first line of each test case contains a natural number nn (1≤n≤1061 \le n \le 10^6) and a string ss, separated by a single space; ss consists of exactly nn characters. The next line contains an integer tt (0≤t≤1050 \le t \le 10^5), the number of changes to apply to ss. Each of the following tt lines contains a natural number p∈[1,n]p \in [1, n] and a character cc, separated by a single space, meaning that the pp-th character of ss must be replaced by cc.

Output

Produce the same output the program above would. In other words, output t+1t + 1 lines, each containing one checksum. The first checksum is computed for the original string ss; each remaining checksum is computed after the corresponding change has been applied to ss.

Hint

An equivalent Java version of the program (which runs even slower):

import java.util.Scanner;

public class Compute {

    static long checksum (byte[] str, int len) {
        int r = 0;
        for (int k=0; k<8*len; k++) {                  // iterate over the bits of str
            if ((r & (1<<31)) != 0) r = (r << 1) ^ 0x04c11db7;
            else r = (r << 1);                         // do some magic
            if ((str[k/8] & 1<<(7-k%8)) != 0)          // if the k-th bit of str is set,
                r ^= 1;                                // flip the last bit of r
        }
        long rr = (r<0 ? r+0x100000000L : r);          // Java has no unsigned int
        return rr;
    }

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        for (int TESTS = in.nextInt(); TESTS>0; TESTS--) {
            int n = in.nextInt();                      // read the input
            byte[] str = in.next().getBytes();         // checksum of the
            System.out.println (checksum(str,n));      // original string
            for (int changes = in.nextInt(); changes>0; changes--) {
                int p = in.nextInt();                  // apply the change
                byte c = in.next().getBytes()[0];
                str[p-1] = c;
                System.out.println (checksum(str,n));  // checksum of the
            } // modified string
        }
    }
}

Examples4

  1. Example 1

    Input
    1
    5 ABcd3
    3
    1 B
    2 A
    1 d
    
    Expected output
    1914964467
    2137468714
    2087137066
    4274181240
    
  2. Example 2

    Input
    1
    1 A
    0
    
    Expected output
    65
    
  3. Example 3

    Input
    1
    1 A
    2
    1 z
    1 0
    
    Expected output
    65
    122
    48
    
  4. Example 4

    Input
    2
    3 abc
    1
    2 X
    4 Test
    0
    
    Expected output
    6382179
    6379619
    1415934836