Obviously you would normally use a database with an efficient algorithm, but conceptually it goes like this:
1) You find the first instance of the account ID in the blockchain. If the account ID does not appear, stop. The account has no firstbits.
2) As a safety, make sure that there are at least six blocks after the block in which the account ID first appears. Otherwise, you risk having the firstbits change if that block is invalidated.
3) You search all previous transactions for the account ID that matches this account ID to as many characters as possible.
4) You output one more character from the account ID than the number of characters matched in step 3. This is the ID's firstbits.
So if the longest match prior to the first appearance of "13xdjsNsf5zJV9tpbxL6iirp1WdettzwyB" starts "13xdjq.." (and thus matches 5 characters), the firstbits for "13xdjsNsf5zJV9tpbxL6iirp1WdettzwyB" is "13xdjs" (the first 6 characters).
To go the other way, you search every account ID that appears in every transaction in the hash chain in appearance order. The first ID whose start matches all of the firstbits is the account ID for that firstbits.