# From Plate to State

This is essentially the inverse of Generate a US License Plate

Challenge: Given a string that matches one of the below license plate formats, output all possible states that match that formatting. In the below table 0 stands for a single digit 0 through 9 inclusive, and A stands for a single letter A through Z inclusive. For the purposes of this challenge, we're ignoring states with complex format rules (like Delaware, which has variable number of digits), and ignoring removal of look-alike letters (e.g., I and 1).

AAA 000: AK, IA, MS, MP, VT
0000: AS
AAA0000: AZ, GA, WA
000 AAA: AR, KS, KY, LA, ND, OR
0AAA000: CA
AA-00000: CT
AA-0000: DC
AAA A00: FL
AA 00000: IL
000A,000AA,000AAA,AAA000: IN
0AA0000: MD
AAA 0000,0AA A00,AAA 000: MI
000-AAA: MN
00A-000: NV
000 0000: NH
A00-AAA: NJ
000-AAA,AAA-000: NM
AAA-0000: NY, NC, PA, TX, VA, WI
AAA 0000: OH
000AAA: OK
AAA-000: PR
000-000: RI
AAA 000,000 0AA: SC
A00-00A: TN
A00 0AA: UT


Examples:

B32 9AG
[UT]

1YUC037
[CA]

285 LOR
[AR, KS, KY, LA, ND, OR] (in any order)

285-LOR
[MN, NM] (in any order)

285LOR
[IN, OK] (in any order)


Rules and Clarifications

• The input string is guaranteed non-empty, and guaranteed to be of one of the above formats
• Behavior if given a format other than the above is undefined
• Input and output can be given by any convenient method
• You can print the result to STDOUT or return it as a function result
• Either a full program or a function are acceptable
• Standard loopholes are forbidden
• This is so all usual golfing rules apply, and the shortest code (in bytes) wins

# JavaScript (ES6),  203 202 201  200 bytes

Saved 1 byte thanks to @NahuelFouilleul

s=>'xMiAZGAWaMIOhNvInxMNNmARKSKYLANDOrNMPrAKIAMIMSMPSCVtAsMdxScRiNhUtDcCtxFlxNYNCPATXVAWiInIlINOkNjCaTn'.match(/[A-Z]*./g)[s.replace(/./g,c=>c<'!'?0:1/c?9:6-~(c+1))*3%47%30].toUpperCase().match(/../g)


Try it online!

## How?

### Input conversion

We turn the input string into an integer $$\n\$$ by applying the following substitutions:

• spaces are replaced with $$\0\$$
• digits are replaced with $$\9\$$
• letters are replaced with $$\7\$$
• hyphens are replaced with $$\6\$$

As JS code:

c < '!' ?        // if c is a space:
0              //   replace it with 0
:                // else:
1 / c ?        //   if c is a digit:
9            //     replace it with 9
:              //   else:
6 - ~(c + 1) //     if c is a hyphen, this gives:
//       6 - ~('-1') --> 6 - 0 --> 6
//     if c is a letter (e.g. 'A'), this gives:
//       6 - ~('A1') --> 6 - ~NaN --> 6 - (-1) --> 7


### Hash function

We then apply the following hash function:

$$f(n) = ((3\times n) \bmod 47) \bmod 30$$

This gives a unique ID in $$\[1..29]\$$. There's actually a collision between 000AA and AAA000 which both result in $$\24\$$, but that's perfectly fine as these plate formats are used in Indiana exclusively.

 format     | n        | * 3       | mod 47 | mod 30 | states
------------+----------+-----------+--------+--------+----------------------
'AAA 000' |  7770999 |  23312997 |   10   |   10   | AK,IA,MI,MS,MP,SC,VT
'0000' |     9999 |     29997 |   11   |   11   | AS
'AAA0000' |  7779999 |  23339997 |   32   |    2   | AZ,GA,WA
'000 AAA' |  9990777 |  29972331 |    8   |    8   | AR,KS,KY,LA,ND,OR
'0AAA000' |  9777999 |  29333997 |   28   |   28   | CA
'AA-00000' | 77699999 | 233099997 |   19   |   19   | CT
'AA-0000' |  7769999 |  23309997 |   18   |   18   | DC
'AAA A00' |  7770799 |  23312397 |   21   |   21   | FL
'AA 00000' | 77099999 | 231299997 |   25   |   25   | IL
'000A' |     9997 |     29991 |    5   |    5   | IN
'000AA' |    99977 |    299931 |   24   |   24   | IN
'000AAA' |   999777 |   2999331 |   26   |   26   | IN,OK
'AAA000' |   777999 |   2333997 |   24   |   24   | IN
'0AA0000' |  9779999 |  29339997 |   12   |   12   | MD
'AAA 0000' | 77709999 | 233129997 |   33   |    3   | MI,OH
'0AA A00' |  9770799 |  29312397 |    1   |    1   | MI
'000-AAA' |  9996777 |  29990331 |    7   |    7   | MN,NM
'00A-000' |  9976999 |  29930997 |   34   |    4   | NV
'000 0000' | 99909999 | 299729997 |   46   |   16   | NH
'A00-AAA' |  7996777 |  23990331 |   27   |   27   | NJ
'AAA-000' |  7776999 |  23330997 |    9   |    9   | NM,PR
'AAA-0000' | 77769999 | 233309997 |   23   |   23   | NY,NC,PA,TX,VA,WI
'000-000' |  9996999 |  29990997 |   15   |   15   | RI
'000 0AA' |  9990977 |  29972931 |   44   |   14   | SC
'A00-00A' |  7996997 |  23990991 |   29   |   29   | TN
'A00 0AA' |  7990977 |  23972931 |   17   |   17   | UT


### State encoding

All state patterns are joined together into a single string, with each pattern ending with a lower case letter. Empty slots are filled with an arbitrary x.

[ [], [ 'MI' ], [ 'AZ', 'GA', 'WA' ], [ 'MI', 'OH' ], ... ] --> 'xMiAZGAWaMIOh...'


We split them back into an array of strings with match(/[A-Z]*./g) and pick the correct one according to $$\f(n)\$$.

Finally, the pattern itself is converted to full upper case and split into groups of 2 characters.

• I love how you always come up with such an elegant mathematical solution to a string-related problem. :) – AdmBorkBork Oct 2 '19 at 17:51
• D'oh! I'm not having a good day today, am I?! – Shaggy Oct 2 '19 at 17:53
• Very clever method! The two plate formats that collide are Indiana, not Illinois. – BradC Oct 2 '19 at 17:58
• [A-Z]*. instead of .*?[a-z] should save one byte – Nahuel Fouilleul Oct 3 '19 at 8:11
• @NahuelFouilleul I've done that mistake before... Thanks! – Arnauld Oct 3 '19 at 8:20

# T-SQL, 475 bytes

SELECT STUFF(value,1,8,'')
FROM STRING_SPLIT('000 0000NH|000 055 SC|000 555 AR,KS,KY,LA,ND,OR|0000    AS|000-000 RI|0005    IN|00055   IN|000555  IN,OK|000-555 MN,NM|005-000 NV|055 500 MI|0550000 MD|0555000 CA|500 055 UT|500-005 TN|500-555 NJ|55 00000IL|55-0000 DC|55-00000CT|555 000 AK,IA,MI,MS,MP,SC,VT|555 0000MI,OH|555 500 FL|555000  IN|555-000 NM,PR|5550000 AZ,GA,WA|555-0000NY,NC,PA,TX,VA,WI','|')
,i WHERE v LIKE TRIM(REPLACE(REPLACE(LEFT(value,8),5,'[A-Z]'),0,'[0-9]'))


Line breaks are for readability only.

Limited to SQL 2017 or higher by use of the TRIM function. SQL 2016 (required for STRING_SPLIT), is possibly by substituting RTRIM at the cost of 1 byte.

Input is taken via pre-existing table $$\i\$$ with varchar field $$\v\$$, per our IO rules

I'm basically doing a reverse LIKE match: I expand the pattern of each plate to a full wildcard pattern matching string like '[A-Z][0-9][0-9] [0-9][A-Z][A-Z]', then compare to the input value, and return the matching states (which is combined into a single field).

Might be able to save some more space by GZIP'ing the lengthy string; I'll see if that helps...

# Perl 5(-p), 165 bytes

Port of @Arnauld's Javascript answer, also upvote him.

y/0-8/9/;y/- A-Z/607/;$_=('xMiAZGAWaMIOhNvInxMNNmARKSKYLANDOrNMPrAKIAMIMSMPSCVtAsMdxScRiNhUtDcCtxFlxNYNCPATXVAWiInIlINOkNjCaTn'=~/[A-Z]*./g)[$_*3%47%30];s/../\U$& /g  Try it online! # Charcoal, 177 bytes §⪪”}∧▶⧴βμÞ∕×peＨ✂d‽n➙ＭR⁶↙↷◨5⁶;πＮM﹪θＷ:¡✂⧴O^(P↷kⅉχＲ⁺≔º↶∨§º⊞Ｂ⎚×p↔Ｌ\²‖6'⁶⁹‴XüR⦃N4U⊙ＹＦ⁻ＺＭχLＳ⁸ＣX\hγ”;⌕⪪”{⊟“◨⦄»Ｕ>⌕⁻9“]±R▷Ｑ↔θü&$▷l⁹Ｚ⁼¡⁷×À›¶aA*βＺ³δ¡⟲²Y«№⌕TμN»πX·ＳΣ"εl⊙=3✂S?Ｋ”;⭆Ｓ⎇№αιA⎇Σι⁰ι


Try it online! Link is to verbose version of code. Explanation:

                 Ｓ              Input string
⭆               Map over characters and join
⎇             If
ι          Current character
№α           Is an uppercase letter
A         Then literal string A
⎇        Else if
ι      Current character
Σ       Is non-zero
⁰     Then digit 0
ι    Else original character
⌕                       Find index in
”...”                 Compressed string of plates
⪪     ;                Split on semicolons
§                               Index into
”...”                         Compressed string of states
⪪     ;                        Split on semicolons
Implicitly print


Try all test cases! Link is to verbose version of code. (Slightly different code needed to process multiple cases.)

Unsurprisingly a port of @Arnauld's solution is much shorter at only 121 bytes:

§⪪”}∧↨¦↑↧‴q⁰mπi3ＪE⪫³yＳ⪪c)？0≦·ê⊞Þ«ⅉ⁺&±<pAＲιaγ1A↑1L¶⟧/)Vº;Π∧,b✂≦¤ⅉαX⊕|″IνB[w∕¦Ｈ´Gγ§ν⟲^π!⪪¶ςbFü⊟»2”;﹪×³Ｉ⭆Ｓ⎇№αι⁷⎇⁼ι ⁰⎇⁼ι-⁶9⁴⁷


Try it online! Link is to verbose version of code. The second modulo by 30 is implicit in the indexing into the array.

# Python 3, 382 378 bytes

import re;f=lambda p,l='[A-Z]',r=re.sub,f=re.findall:f(l+'{2}',f(r(l,'1',r('[0-9]','0',p))+l+'+','0000AS0001IN00011IN000111INOK111000IN000 011SC000 111ARKSKYLANDOR000-000RI000-111MNNM001-000NV011 100MI0110000MD0111000CA100-001TN100-111NJ11-0000DC111 100FL111 000AKIAMIMSMPSCVT111-000NMPR1110000AZGAWA11 00000IL11-00000CT111 0000MIOH111-0000NYNCPATXVAWI1100 011UT000 0000NH')[0])


Try it online!

Replaces numbers with 0 and letters with 1, then searches the string for the plate followed by a string of letters. Then it simply returns every non-overlapping pair of letters in that string.

Not the most byte efficient, but a good start (maybe).

I enjoy challenges based on information that can't just be generated.

# 05AB1E, 176 bytes

•1–ºʒÉQ÷¦PαN]lā?¨ìÎ₆™@ΔîÅλEŸʒ»ú<Å§a–½ã…ôkƒ¼½Ü%-ò∊aÍÙ•44374в4вε©gIgQi…'-Q'd'a„ðQ)VIεY®Nèè.V}Pë0].•=#îYn«ÈO4êʒIWj∊bÛˆ_ãZÑ"yŠótм‰иÔN–HδÖc°ìSJ9Ç\}ζÎäǝÑïÒ∞V.÷ζkÚ"¿Õнα£!ÉªB…žä•#sÏ2ô


Try it online!

•1–ºʒÉQ÷¦PαN]lā?¨ìÎ₆™@ΔîÅλEŸʒ»ú<Å§a–½ã…ôkƒ¼½Ü%-ò∊aÍÙ•44374в4в
push all patterns as base 4 integers (0="-", 1=number, 2=letter, 3=" ")

ε                       for each pattern
©                       copy it for later use inside another for for-loop
gIgQi                   if it has the same length, as input
…'-Q'd'a„ðQ)V           store ["'-Q", "d", "a", "ðQ"] in Y (05AB1E codes for "equals '-', is positive, is letter and equals ' ')
Iε                      for each letter of input
Y®Nèè                   get the 05AB1E code corresponding to the current index of the pattern
.V                      run it
}
P                       check if all positions of that pattern were true
ë                       else
0                       push false
]

.•=#îYn«ÈO4êʒIWj∊bÛˆ_ãZÑ"yŠótм‰иÔN–HδÖc°ìSJ9Ç\}ζÎäǝÑïÒ∞V.÷ζkÚ"¿Õнα£!ÉªB…žä•#
push list of states matching the pattern

sÏ                      get the entry of that list, that is true in the other list
$$$$2ô                        split into groups of 2 letters and print
`