1. 程式人生 > 其它 >【CodeForces 1003F】【map編碼+暴力kmp】

【CodeForces 1003F】【map編碼+暴力kmp】

技術標籤:題目字串

題目連結
題目大意是給一個單詞序列,裡面如果出現重複的單片語(兩個及以上連續單詞),就可以將單詞組裡面的單詞用一個字母替換,總長度就會減小。每次只有一個重複出現的單片語可以替換,求總長度的最小值。例如“to be or nor to be”就可以替換成"TB or not TB",總長度就由18變為12。

我一開始想的是用“.”替換空格,然後在新的字串中找最長重複子串。上網搜了一下可以用字尾陣列求,也可以二分最長重複子串的長度。

但是寫著寫著發現不可行,這樣有可能最後得到的子串不是完整的單詞。

看了一篇題解,上面給的解法是給單詞編碼後暴力列舉子串(因為總共不到300個單詞),用kmp求出子串在原串裡面出現的次數,然後計算替換後的長度,取最小值。這樣的話複雜度是n^3,對於300的資料範圍來說完全可以。

我一開始以為對單詞編碼需要用26進位制轉10進位制,但是字串總長度是10^5,這樣編碼long long都不夠存,後來一看用map就可以,相當簡單的編碼hhh
map要注意key和value的順序,key在前,是不可重複的,尋找的依據。這裡用string作為key。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <map>
#include <string>
#include <algorithm>
#include <climits>
using namespace std; int len[305]; map<string, int> m; int a[305], b[305], nextval[305]; int cnt = 0; int n; void getnext(int *b, int *nextval, int size) { int i = 0, j = -1; nextval[0] = -1; while (i < size) { if (j == -1 || b[i] == b[j]) { i++, j++;
if (b[i] == b[j]) { nextval[i] = nextval[j]; } else { nextval[i] = j; } } else { j = nextval[j]; } } } int counttimes(int *a, int *b, int *nextval, int size) { int t = 0; // for (int i = 0; i < size; i++) // { // printf("%d ", nextval[i]); // } // printf("n = %d, size = %d\n", n, size); int i = 0, j = 0; while (i < n) { if (j == -1 || a[i] == b[j]) { i++, j++; if (j == size) { t++; j = 0; } } else { j = nextval[j]; } } return t; } int main() { scanf("%d", &n); for (int i = 0; i < n; i++) { string s; cin >> s; if (!m.count(s)) { m[s] = cnt; a[i] = cnt; cnt++; } else { a[i] = m[s]; } len[i] = s.size(); } // for (int i = 0; i < n; i++) // { // printf("%d ", a[i]); // } // printf("\n"); int totallen = 0; for (int i = 0; i < n; i++) { totallen += len[i] + 1; } totallen -= 1; if (cnt == n) { printf("%d\n", totallen); return 0; } int ans = totallen; for (int l = 0; l < n; l++) { for (int r = l; r < n; r++) { int size = r - l + 1; int partlen = 0; for (int i = l; i <= r; i++) { b[i - l] = a[i]; partlen += len[i] + 1; } partlen -= 1; getnext(b, nextval, size); int t = counttimes(a, b, nextval, size); if (t > 1) { //printf("%d\n", t); ans = min(ans, totallen - partlen * t + (size) * t); //printf("ans = %d\n", ans); } } } printf("%d\n", ans); return 0; }

我寫getnext的時候犯沙茶錯誤了QAQ,寫了個b[i] == b[i]愣是半天沒看出來TVT 以後一定在寫的時候要好好注意,不然的話超級浪費時間。