1. 程式人生 > >NOIP複賽複習(十四)字串演算法鞏固與提高

NOIP複賽複習(十四)字串演算法鞏固與提高

一、Trie

 

1.定義

通過字串建成一棵樹,這棵樹的節點個數一定是最少的。例如:4個字串"ab","abc","bd","dda"對應的trie樹如下:

其中紅色節點表示存在一個字串是以這個點結尾的。 

一個性質:在樹上,兩個點u,v滿足uv的祖先,那麼u代表的字串一定是v代表的字串的字首。

 

2.Trie樹的插入

可以從根節點出發,每次沿著要走的字串往下走,若沒有則建立新節點。 

假如所有字串的長度之和為n,構建這棵trie樹的時間複雜度為O(n)


int root=1;

int cnt=1;

int p[i][j];//表示從i這個節點沿著j這個字元走,能走到哪個點,走不到就是0

char s[];//儲存字串

for(int i=1;i<=n;i++)

{

   scanf("%s",s);

    int len=strlen(s);

    int now=root;//now表示走到的當前節點,now的初始值為根節點

    for(intj=0;j<len;j++)

    {

       if(p[now][s[j]]>0){

           now=p[now][s[j]];//如果能沿著j節點往下走,直接往下走

        }

        else{

           pow[now][s[j]]=++cnt;

           now=p[now][s[j]];

        }

    }

    v[now]++;//記錄now這個節點被訪問的次數

}

 

3.Trie樹的查詢

可以看出trie樹中每個節點表示其中一個字串的字首,在做題過程中往往通過這個性質來得到較好的時間複雜度。

 

4.一個例題

給定n個互不相同的串,求存在多少對數(i,j)(共n2對)滿足第i個串是第j個串的字首。 

所有串的長度之和≤500000 

解題:根據性質,在樹上,兩個點u,v滿足uv的祖先,那麼u代表的字串一定是v代表的字串的字首 

我們要滿足一個串是另一個串的字首,也就是說,在trie樹上,這個串對應的位置是另一個串對應的位置的祖先。 

構建這棵trie樹,然後我們列舉每個紅色點,它對答案的貢獻是以它為根的子樹中紅色節點的個數之和。這個東西可以在一開始遍歷這棵樹預處理出來! 

時間複雜度是線性的。

 

void dfs(int x)

{

    if(v[x]) sum[x]++;

    for(chari='a';i<='z';i++)

    {

        if(p[x][i])//從當前x沿著i這個字元走還能往下走

        {

           dfs(p[x][i]);//往下走

           sum[x]+=sum[p[x][i]];//累加貢獻sum

        }

    }

}

dfs(1);//從根節點開始


5.USACO的某題

給定n個串,重排字元之間的大小關係,問哪些串有可能成為字典序最小的串。 

所有字串的長度之和<=100000 

例如,有一個字串,裡面只有'a'~'z'這些字元,預設地,'a'是最小的,'z'是最大的。但是我們可以重新定義字元間的大小關係,比如這樣:b<c<d<y<x<z,從而我們對於一些字串,就按照我們新定義的大小關係來比較字典序大小。 

舉個栗子: 

三個字串"aab","aba","baa",總共只出現了兩個字元'a''b',所以字元間的大小關係要麼是a<b要麼是a>b,假設a<b,則第一個串"aab"就是字典序最小的串;假設b<a,則第三個串"baa"就是字典序最小的串,但是對於第二個串,無論我們怎麼定義字元間的大小關係,都不可能成為三個字串中字典序最小的。 

解題:首先對這n個串構建trie樹,之後對每個串,從根走向它,這個路程中遇到的所有兄弟在字典序下都比它大。 

對於每個串,從前往後,直接確定這個字元的大小關係,判斷是否是字典序最小。 

以下兩個串都是有可能成為字典序最小的串的: 

abcd…xyza 

abcd…xyzb 

所以有:uv的字首,則v一定不是字典序最小的串。 

現在問題來了:對於每個串,在什麼條件下,是字典序最小的?? 

來個栗子:有一些字串("aabc""aac""aad""ac""adb""aabb"),構造trie樹如下:

 

對於"aabc"這個串,往下遍歷: 

從深度為2的那一層,我們可以得到:a<c,a<d 

從深度為3的那一層,我們可以得到:b<c,b<d 

從深度為4的那一層,我們還可以得到:c<b 

綜上所述:我們得到了一堆的關係:a<c,a<db<c,b<dc<b,容易看出,這些關係是存在矛盾的,所以無解->"aabc"是不可能成為字典序最小的串。 

所以要想有解,這一堆的關係必須滿足兩個條件:不存在矛盾不存在環 

總的來說,就是判斷這一堆的關係是否存在拓撲序! 

最多有26×26個大小關係,而最多有100000個字串,所以時間複雜度最大為:O(26×26×100000)

 

二、KMP演算法

 

給定兩個字串A,B,判斷T是否為S的子串(變式:尋找子串B在串A中的位置)。 

要求一個O(|A|+|B|)的做法。 

通常稱A為目標串(或主串),B為模式串。 

演算法過程: 

我們假設串A的長度為n,串B的長度為m,每個字串的開頭下標預設為1

 

定義兩個變數ij,這兩個變數共同表示:A[i-j+1~i]B[1~j]均匹配,即:A中以第i個字元結尾的、長度為j的字串,和B從頭開始長度為j的字串完全匹配。

 

繼續往下匹配:如果i+1j+1不匹配。

 

現在,就是用到了KMP演算法的核心:它對這一情況的處理方式是減少j,就相當於將子串向右平移。 

平移的目的是為了讓“A[i-j+1~i]B[1~j]均匹配這個條件重新滿足。

在上圖中,j一直減小到了0,因為向右平移的過程中,始終不能讓這個條件滿足(最右邊"?"部分已經越界) 

但有時候,將j減少一點點之後,是可以重新滿足條件的,例如:

 

那麼我們將j7減小到4時,有:

 

這樣就可以完全匹配啦!但是後面還有沒有匹配的機會我們就不管了,至少我們已經保證A[4~7]B[1~4]完全匹配上了。 

現在考慮一個問題:我們每次把j減小1(一位一位地平移B字串),這樣太慢了,我們在這裡預處理一個next[]陣列,表示當j匹配不下去的時候,我們可以把j減少到next[j],繼續嘗試匹配。 

預處理過程:讓j自己和自己匹配一下,一旦匹配發現B[k-m+1~k]  B[1~m] 匹配,則說明在AB匹配過程中,j等於k匹配不下去時,j可以嘗試減小到m 

過程如下:

 

/**************************************///靚麗的分界線

 

一些程式碼:

 

/*核心內容*/

for(int i=1,j=0;i<=n;i++)

{

   while(j&&B[j+1]!= A[i]) j=next[j];

    if(B[j+1]==A[i]) j++;

    if(j==m)

    {

       printf("%d\n", i-j+1);//輸出找到的"B字串在 A中位置"

        //如果要求的是出現次數,這裡也有可能是ans++什麼的

        j=next[j];//讓迴圈進行下去

    }

}

 

for(int i=2,j=0;i<=m;i++)/*預處理next[]陣列*/

{

    while(j&&B[j+1]!=B[i]) j=next[j];

相關推薦

NOIP複賽複習字串演算法鞏固提高

一、Trie樹   1.定義: 通過字串建成一棵樹,這棵樹的節點個數一定是最少的。例如:4個字串"ab","abc","bd","dda"對應的trie樹如下: 其中紅色節點表示存在一個字串是以這個點結尾的。  一個性質:在樹上,兩個點u,

NOIP複賽複習數論演算法鞏固提高

一、數論    1.數   整數、自然數(大於等於0的整數)、正整數(大於0的整數)、負整數、非負整數、非正整數、非零整數、奇數偶數。   2.整除性   設a,b∈Z,如果存在c∈Z並且a=bc,則

NOIP複賽複習基礎演算法鞏固提高

一、倍增演算法:   定義:用f[i][j]表示從i位置出發的2j個位置的資訊綜合(狀態) 一個小小的問題:為什麼是2j而不是3j,5j,…?因為,假設為kj,整個演算法的時間複雜度為(k-1)logk,當k=2時,時間複雜度最小。 這個演算法的三個應用:

NOIP複賽複習動態規劃鞏固提高

經典例題:數字金字塔(Luogu 1216)  寫一個程式來查詢從最高點到底部任意處結束的路徑,使路徑經過數字的和最大。每一步可以走到左下方的點也可以到達右下方的點。 我們現在這裡討論搜尋如何實現:  狀態:目前在第x行第y列 

NOIP複賽複習怎樣才能拿到高分?

摘要 考場策略和程式測試是資訊學競賽中非常重要的環節,很多優秀的選手在很多比賽中總是會在這兩個環節上犯下這樣和那樣的錯誤,導致得到的分數和實力不成正比,最後留下了無盡的遺憾。本文將探討一些這兩個環節上值得注意的地方,提出一些可行的方法,分享一些經驗,以此希望幫助選手們在比賽中發揮水平,減少失

Python/Pandas-字串正則

split拆分常和strip使用 val='a,b,guido' val.split(',') ['a', 'b', 'guido'] pieces=[ x.strip() for x in val.split(',')] pieces ['a', '

讀書筆記——作業的知識點註意事項

png dba 是否 命名 技術分享 發送郵件 fig 命令 name 1、 SQL Server 代理中包含很多的類別,有作業、警報、操作員、代理等,作業屬於其中的一個類別 1、 用自動化數據備份來介紹作業的具體運用規則 ①用命令符啟用sqlserveragen

Spring CloudConfig 配置中心客戶端的使用詳細

active html 提交 acl fail 出現 tee ini sdi 前言 在上一篇 文章 中我們直接用了本應在本文中配置的Config Server,對Config也有了一個基本的認識,即 Spring Cloud Config 是一種用來動態獲取Git、SVN、

Redis 學習筆記Redis Cluster介紹搭建

Redis Cluster 介紹與搭建 1. Redis Cluster介紹 Redis Cluster是Redis的分散式解決方案,在Redis 3.0版本正式推出的,有效解決了Redis分散式方面的需求。當遇到單機記憶體、併發、流量等瓶頸時,可以採用Cl

《瘋狂Java講義》學習筆記類載入機制反射

1、類的載入、連線和初始化 JVM和類 同一個JVM的所有執行緒、所有變數都處於同一個程序裡,它們都使用該JVM程序的記憶體區 當系統出現一下集中情況時,JVM程序將被終止: 程式執行到最後正常結束 程式執行到使用Sytem.exit()或Runti

OpenCV學習筆記——影象結構分析形狀描述ImgProc

OpenCV支援大量的輪廓、邊緣、邊界的相關函式,相應的函式有moments、HuMoments、findContours、drawContours、approxPolyDP、arcLength、boundingRect、contourArea、convexHull、fit

NOIP複賽複習STL容器字串模板

STL容器 STL 容器是一些模板類,提供了多種組織資料的常用方法。常用的STL容器包括pair(組合)、list(列表,類似於連結串列)、vector(向量,類似於陣列)、priority_queue(優先佇列)、set(集合)、map(對映)、stack(棧)等,通過模板的引數

NOIP複賽複習讀寫外掛高精度模板

讀入輸出掛 讀入輸出掛就是逐個字元地讀入資料,從而讓讀入更加快速。輸出掛的原理也是一樣的,都是通過將輸出數字變成輸出字元以加快速度。當然輸入輸出外掛一般用在大量輸入輸出的情況下,這樣價效比才高一些,否則得不償失。 void Rd(int &res){  &nbs

NOIP複賽複習十三圖論演算法鞏固提高

一、圖的儲存   1、鄰接矩陣   假設有n個節點,建立一個n×n的矩陣,第i號節點能到達第j號節點就將[i][j]標記為1(有權值標記為權值),  樣例如下圖:   /*無向圖,無權值*/ i

NOIP複賽複習如何設計測試資料?

有些同學參加一次資訊學比賽之後,自我感覺非常不錯,但是測評結果成績卻並不理想。造成這種情況的原因有多方面,但是我認為其中不可忽視的一大原因就是在寫完程式之後,他們並不知道如何保證程式的正確性。在這裡,我就這個問題提出一點自己的看法。 先從考試的時間分配問題講起。很多同學覺得考試時間很充分,N

NOIP複賽複習STL演算法樹結構模板

STL演算法 STL 演算法是一些模板函式,提供了相當多的有用演算法和操作,從簡單如for_each(遍歷)到複雜如stable_sort(穩定排序),標頭檔案是:#include <algorithm>。常用STL 演算法庫包括:sort快速排序演算法、二分

NOIP複賽複習演算法分析排序模板

演算法分析 演算法分析的目的是預測演算法所需的資源,如計算時間(CPU 消耗)、記憶體空間(RAM 消耗)、通訊時間(頻寬消耗)等,以及預測演算法的執行時間,即在給定輸入規模時,所執行的基本運算元量,或者稱為演算法複雜度。 演算法的執行時間取決於輸入的資料特徵,輸入

NOIP複賽複習程式對拍圖論模板

程式對拍 所謂“對拍”,顧名思義,就是讓兩者相互比對。所謂“兩者”,一是你要測試的程式,二是一個答案在該程式在一定範圍(時間/空間)內結果必定正確的程式(一般是用暴力求解的程式)。對拍一般需要造資料程式(data.exe),保證正確性的暴力對拍程式(test.exe)與測試程式(以moo.e

NOIP複賽複習檔案讀寫數論模板

檔案讀入讀出 假設題目名為“add”,那麼資料夾名為“add”,c++程式名為“add.cpp”,讀入檔名為“add.in”,輸出檔名為“add.out”。四個的拼寫均不可有誤,包括大小寫差異。千萬不要除錯後就忘記修改檔案讀入讀出了。  #include<cstdio&

NOIP複賽複習競賽環境注意事項

一、比賽不提供紙質試題,只提供電子版試題檔案。 該檔案壓縮包儲存在計算機桌面上。監考人公佈密碼後,選手自行解密試題。 試題解壓密碼會影響一個人的心情,一定要一次輸對,注意大小寫,不要邊輸入邊檢查,要對自己有自信。 二、江蘇複賽選手上機可自選windows或linux作業系統。