Zadanie

S jeseňou okrem chladného počasia a padania listov prichádza aj nový školský rok a Adam musel opäť nastúpiť do školy. Keďže sa do školy vrátiť nechcel, vymýšlal všelijaké výhovorky, aby mohol zostať doma (napr. strčil teplomer do teplého čaju; presviedčal rodičov, že je stále august; schoval sa pod posteľ; …). Tie sa mu však neprepiekli a do školy išiel aj tak.

Ako tam sedel, už na druhej vyučovacej hodine, uvedomil si, že to napokon nie je až také zlé. Čo ale čert nechcel, cez veľkú prestávku si spolužiaci začali robiť srandu z jeho štíhlej stavby tela. Na lavici mu pristál papierik so slovami Madam Adam. Adama to trošku rozhodilo a spýtal sa učiteľky, či by sa dalo ísť domov. Učiteľka mysliac, že sa pýtal na záchod, ho s úsmevom na perách pustila.

Po príchode domov si ešte raz otvoril papierik s odkazom. Ako sa naň pozeral, uvedomil si zaujímavú vec – madamadam sa číta rovnako spredu aj zo zadu. Takáto vlastnosť sa Adamovi veľmi zapáčila a vymyslel si takých slov viac. Ba dokonca, vymyslel ku takýmto slovám zaujímavú úlohu.

Úloha

Adam vymyslel n stringov s k znakmi, ktoré sa čítajú rovnako spredu aj zo zadu. String obsahuje znaky a-z, A-Z a 0-9. Okrem toho, v každom stringu je každý unikátny znak maximálne dvakrát. Vašou úlohou je zistiť, koľkými rôznymi spôsobmi sa dajú vymazať znaky zo stringu, aby sa nečítal rovnako spredu aj zo zadu.

Formát vstupu

Na prvom riadku vstupu je číslo n, ktoré reprezentuje počet otázok. Potom nasleduje n riadkov a na každom z nich je string dĺžky k.

Formát výstupu

Na každý riadok vypíšte číslo, koľkými rôznymi spôsobmi vieme vymazať znaky v stringu, aby sa nedal prečítať rovnako spredu aj zo zadu. Keďže odpoveď môže byť veľmi veľká, vypisujte ju modulo \(10^9 + 7\).

Obmedzenia

\(4\) sady vstupov po \(2\) body. Platia v nich nasledovné obmedzenia:

Sada \(1\) \(2\) \(3\) \(4\)
\(1 \leq n \leq\) \(10\) \(100\) \(1\,000\) \(10\,000\)
\(1 \leq k \leq\) \(10\) \(25\) \(75\) \(125\)

Príklad

Input:

1
AbbA

Output:

6

Po vymazaní znakov zo stringu AbbA je týchto 6 možných stringov, ktoré sa nečítajú rovnako spredu aj zo zadu: Abb, Ab, Ab, bbA, bA, bA. Na prvý pohľad sa zdá, že stringy Ab a Ab sú rovnaké, lenže jeden string obsahuje znak b na prvom indexe a druhý obsahuje znak b na druhom indexe, čiže sú rôzne. Tak isto aj bA a bA.

Input:

2
DaL00LaD
bysAsyb

Output:

210
98

Zo stringu DaL00LaD sa dajú vymazať znaky 210 spôsobmi a zo stringu bysAsyb sa dajú vymazať znaky 98 spôsobmi, aby sa nečítali rovnako spredu aj zo zadu.

Pomalé riešenie

Najjednoduchšie riešenie tejto úlohy je vyrobiť si všetky možnosti premazaných substringov, ktoré zodpovedajú zadaniu. Akonáhle máme tieto substringy, tak cez každý prejdeme for cyklom, ktorý sa pozerá na znaky z obidvoch koncov substringu. Ak sa znaky spredu aj zozadu zhodujú, tak substring preskočíme. Ak sa nejaký/é znak/y nezhodoval/i, tak si započítame \(+1\) do možností. Takto prejdeme cez všetky možné substringy a zistíme koľko z nich sa nečíta rovnako spredu aj zozadu.

Rýchle riešenie

Lepší prístup k tejto úlohe je zistiť si celkový možný počet substringov a od neho odčítať počet tých substringov, ktoré sa čítajú rovnako spredu aj zozadu. Celkový počet substringov je \(2^k\), pretože máme dokopy k znakov v stringu a každý z nich bude alebo nebude v substringu. Teraz ešte treba zistiť, čo od toho odčítať.

Existujú dva druhy stringov, ktoré nám vedia prísť na vstupe – párnej a nepárnej dĺžky. Najskôr sa budeme venovať stringom párnej dĺžky. Tie pozostávajú z dvojíc rovnakých znakov na opačných stranách stringu. Keď vymažeme nejaké znaky zo stringu, tiež máme dve možnosti novovzniknutého substringu – bude párnej alebo nepárnej dĺžky. Pozrime sa na všetky substringy párnej dĺžky. Predstavme si, že v strede stringu je zrkadlo. Keďže string sa dá čítať rovnako spredu aj zozadu, tak ľavá a pravá polovica vyzerajú rovnako, len zrkadlovo – DaL0|0LaD. Ak vymažeme znaky tak, že nám stále ostane párny počet znakov v substringu, tak vieme jednoducho zistiť, koľko je takých, ktoré sa čítajú rovnako spredu aj zozadu. Ak vymažeme znak na nultom indexe, tak musíme vymazať aj na poslednom – aL00La; ak na prvom, tak aj predposlednom – DL00LD; … . Takto sa dostaneme ku vzorcu \(2^{k/2}\). Pretože ak vymažeme ktorýkoľvek znak z prvej polovice stringu, tak vymažeme aj jeho dvojicu v druhej polovici. Keďže mažeme dvojice, tak máme “polovičnú” dĺžku stringu – \({k/2}\); a každá dvojica bude alebo nebude v substringu. Berieme do úvahy aj to, že nevymažeme ani jeden znak alebo vymažeme všetky.

Ešte potrebujeme odčítať počet substringov nepárnej dĺžky, ktoré sa čítajú rovnako spredu aj zozadu. Je to jednoduchšie ako sa na prvý pohľad zdá. Majme všetky substringy párnej dĺžky z minulého odseku. Z dvojice znakov, ktorá je v strede vymažeme jeden znak a máme substring nepárnej dĺžky, ktorý sa číta rovnako spredu aj zozadu – napr. aL0La. Na čo však nesmieme zabudnúť je to, že môžeme vymazať pravý alebo ľavý znak, čo sa počíta ako dva rôzne substringy. Takže dokopy máme \(2 \cdot 2^{k/2}\) možnoných substringov nepárnej dĺžky, ktoré sa čítajú rovnako spredu aj zozadu. Čo však nie je úplne pravda. Ako bolo aj predtým povedané, v \({k/2}\) sa ráta za možnosť aj prázdny string. Z prázdneho stringu však nevieme nič odčítať, takže vzorec bude vyzerať takto: \(2 \cdot (2^{k/2} - 1)\). Celkový vzorec na vypočítanie všetkých možných párnych stringov, ktoré sa nedajú prečítať rovnako spredu aj zozadu je \(2^k - 2^{k/2} - 2 \cdot (2^{k/2} -1)\).

Pri stringu nepárnej dĺžky sa správame podobne ako pri párnej dĺžky. Tie tiež pozostávajú z dvojíc rovnakých znakov na opačných stranách stringu, ale majú jeden znak v strede navyše – bysAsyb. Predstavme si, že ten znak v strede navyše tam nie je – byssyb. V takom prípade sme opäť naspäť pri stringu párnej dĺžky a vieme použiť vzorec \(2^k - 2^{k/2} - 2 \cdot (2^{k/2} -1)\). Avšak, od celkového počtu ešte potrebujeme odčítať možnosti, kedy sme znak v strede nevymazali. To je v podstate to isté ako keď vymazávame znaky tak, aby sme mali substringy párnej dĺžky. Budeme vymazávať dvojice rovnakých znakov a vždy necháme stredný znak – ysAsy, bsAsb, bAb, … . Takže od celého vzorca ešte odčítame \(2^{k/2}\), a teda výsledný vzorec pre string nepárnej dĺžky bude vyzerať takto: \(2^k - 2^{k/2} - 2 \cdot (2^{k/2} -1) - 2^{k/2}\).

Samozrejme, nakoniec nesmieme zabudnúť na modulo \(10^9 + 7\) a implementovať to cez rýchle modulovanie.

Časová zložitosť je \(O(n \cdot \log k)\), pretože počet možností vypočítame v čase \(\log k\) a vypočítame ich \(n\)-krát, lebo máme \(n\) otázok. Pamäťová zložitosť je \(O(k)\).

MOD = 1000000007

def moznosti(k):
    if k % 2 == 0:
        return (pow(2, k, MOD) - pow(2, (k//2), MOD) - 2*(pow(2, (k//2), MOD) -1)) % MOD
    else:
        return (pow(2, k, MOD) - pow(2, (k//2), MOD) - 2*(pow(2, (k//2), MOD) -1) - pow(2, (k//2), MOD)) % MOD

n = int(input())
for _ in range(n):
    slovo = input()
    k = len(slovo)
    print(moznosti(k))

Diskusia

Tu môžte voľne diskutovať o riešení, deliť sa o svoje kusy kódu a podobne.

Pre pridávanie komentárov sa musíš prihlásiť.