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
Sú \(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)\).
= 1000000007
MOD
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
= int(input())
n for _ in range(n):
= input()
slovo = len(slovo)
k 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ť.