sql >> Databasteknik >  >> RDS >> Mysql

Scrabble ordsökare med jokertecken

Det gör du inte. En relationsdatabastabell är inte en lämplig datastruktur för att lösa detta problem så effektivt som du behöver.

Det du gör istället är att du bygger en försök datastruktur från ordboken (eller, om du är riktigt kär, bygger du en dawg -- en riktad acyklisk ordgraf -- som är en sorts komprimerad försök.)

När du väl har en trie/dawg blir det väldigt billigt att testa varje ord i ordboken mot ett givet ställ, eftersom du kan "beskära ut" hela enorma grenar av ordboken som stället omöjligt kan matcha.

Låt oss titta på ett litet exempel. Anta att du har ordboken "OP, OPS, OPT, OPTS, POT, POTS, SOP, SOPS, STOP, STOPS" Från det bygger du detta försök:(Noder med en $ är de som är markerade som "ord kan sluta här" .

           ^root^
           /  |  \
         O    P    S
         |    |   / \
         P$   O  O   T   
        / \   |  |   |
       T$  S$ T$ P$  O
       |      |  |   |
       S$     S$ S$  P$
                     |
                     S$

och du har racket "OPS" -- vad gör du?

Först säger du "kan jag gå ner för O-grenen?" Jo det kan du. Så nu är problemet att matcha "PS" mot O-grenen. Kan du gå ner i P-undergrenen? Ja. Har den en slutmarkör för ordet? Ja, så OP är en match. Nu är problemet att matcha "S" mot OP-grenen. Kan du gå ner för T-grenen? Nej. Kan du gå ner för S-grenen? Ja. Nu har du det tomma stället och du måste matcha det mot OPS-grenen. Har den en slutmarkör för ordet? ja! Så OPS matchar också. Gå nu tillbaka till roten.

Kan du gå ner för P-grenen? Ja. Nu är problemet att matcha OS mot P-grenen. Gå ner i PO-grenen och matcha S -- det misslyckas. Gå tillbaka till roten.

Och igen, du ser hur det går. Så småningom går vi ner i SOP-grenen och hittar ett slut på ordet på SOP, så "SOP" matchar detta ställ. Vi går inte ner för ST-grenen eftersom vi inte har ett T.

Vi har provat alla möjliga ord i ordboken och upptäckt att OP, OPS och SOP alla matchar. Men vi behövde aldrig undersöka OPTS, POTS, STOP eller STOPS eftersom vi inte hade ett T.

Ser du hur denna datastruktur gör den mycket effektiv? När du har fastställt att du inte har bokstäverna på hyllan för att börja av ett ord behöver du inte undersöka något ordboksord som börjar med den början. Om du har PO men inget T, behöver du inte undersöka POTSSHRD eller POTASI eller POTASH eller POTLATCH eller POTABLE; alla dessa dyra och fruktlösa sökningar försvinner väldigt snabbt.

Att anpassa systemet för att hantera "vilda" brickor är ganska enkelt; om du har OPS?, kör då bara sökalgoritmen 26 gånger, på OPSA, OPSB, OPSC... Det borde vara tillräckligt snabbt för att det är billigt att göra det 26 gånger (eller att göra det 26 x 26 gånger om du har två blanksteg. )

Detta är den grundläggande algoritmen som professionella Scrabble AI-program använder, även om de naturligtvis också måste hantera saker som styrelseposition, rackhantering och så vidare, vilket komplicerar algoritmerna något. Denna enkla version av algoritmen kommer att vara tillräckligt snabb för att generera alla möjliga ord på ett ställ.

Glöm inte att du naturligtvis bara behöver beräkna trie/dawg en gång om ordboken inte förändras över tiden. Det kan vara tidskrävande att bygga testet ur ordboken, så du kanske vill göra det en gång och sedan komma på något sätt att lagra försöket på disk i en form som är mottaglig för att snabbt återskapa den från disk.

Du kan optimera minnesanvändningen genom att bygga en DAWG av försöket. Lägg märke till hur det är många upprepningar eftersom på engelska, många ord slutar samma sak, precis när många ord börjar det samma. Trie gör ett bra jobb med att dela noder i början men ett uselt jobb med att dela dem i slutet. Du kan till exempel lägga märke till att mönstret "S$ utan barn" är extremt vanligt, och förvandla försöket till:

           ^root^
          / |  \
        O   P    S
        |   |   / \
        P$  O  O   T   
       /  \ |  |   |
      T$  | T$ P$  O
      |    \ | |   |
       \    \| /   P$
        \    |/    |
         \   |    /
          \  |   /  
           \ |  /
            \| /  
             |/
             |       
             S$

Sparar en hel hög med noder. Och då kanske du märker att två ord nu slutar på O-P$-S$, och två ord slutar på T$-S$, så att du kan komprimera det ytterligare till:

           ^root^
           / | \
          O  P  S
          |  | / \
          P$ O \  T   
         /  \|  \ |
         |   |   \|
         |   |    O
         |   T$   |
          \  |    P$
           \ |   /
            \|  /  
             | /
             |/   
             S$

Och nu har vi den minimala DAWG för denna ordbok.

Mer läsning:

http://dl.acm.org/citation.cfm?id=42420

http://archive.msdn.microsoft.com/dawg1

http://www.gtoal.com/wordgames/scrabble.html



  1. log4net-loggningsfel i mysql-databasloggning

  2. MySQL mysql_tzinfo_to_sql-program

  3. Mysql AVG för att ignorera noll

  4. Hur sqrt() fungerar i PostgreSQL