P1666字首單詞

2022-05-31 13:09:09 字數 1446 閱讀 3779

題目傳送門點我傳送

ⅰ.字典樹+樹型dp

非常奇妙的一種解法

第一部分:構建樹

先對來的單詞讀入,插入字典樹

然後對於一顆字典樹,其實是有很多無用邊的,所以我們需要刪去一些邊

刪去非單詞節點和非單詞節點之間的邊,其實就是下面這個函式

void rebuild(int now,int fa)

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

}

第二部分:樹型dp

對於每一棵子樹而言,右選和不選兩種方案

選,則子樹上的節點都不能再選,即為

\(dp[i][1]=1\)

不選,則子樹上的節點可選可不選

\(f[i][0] = \prod_(f[j][0]+f[j][1])\)

因為是計算方案,決策之間是彼此聯絡的,所以是相乘

#include using namespace std;

typedef long long ll;

const int maxn=5009;

int n;

int tree[maxn][27];int isok[maxn],tot;

void insert(string s)

for(int i=1;i<=26;i++) }

ll dp[maxn][3];

void ddp(int now)//開始dp

ddp(0);//樹型dp

cout<ⅱ.線性dp

我們預處理乙個\(f[i][j]\)表示第i個單詞與第j個單詞是否能共存,

然後考慮乙個\(dp[i]\)表示在前i個單詞中,必須包含第i個單詞的子集個數,首先\(dp[i]=1\)(只包含自己乙個元素的乙個子集方案數)

那麼如果前面存在乙個\(j,並且\(f[i][j]=1\)(即i與j可以共存),那麼j所有的子集方案數加上乙個i元素就形成了對應新的方案,那麼狀態就由j轉移到i了,最後,直接累計\(dp[1....n]\)即可,當然最後還要算上空集喲。

但是,這麼做的前提是單詞必須先排序

為什麼呢??因為在\(dp[j]\)的方案中,可能有是\(a[i]\)字首的單詞,那我們就不能轉移

但如果按照字典序排過之後,就不存在這種問題。

#include using namespace std;//dp[i]為必須包括i的個數

typedef long long ll;

ll vis[59][59],dp[59];

string a[59];

bool pan(int l,int r)

for(int i=2;i<=n;i++) }

ll ans=0;

for(int i=1;i<=n;i++) ans+=dp[i];

cout<}

題解 P1666 字首單詞

考慮到沒有相同的字串,不難發現這是乙個樹形dp,在字典樹上,一旦某個節點被選中,其子樹中的任何節點都不能被選中。如果使用字串比較建圖的話,需要注意去除多重的邊。include include include include includeusing namespace std typedef uns...

洛谷 P1666 字首單詞

首先空集合也是答案,所以樣例是對的 那麼我們思考 假如兩個串ab a b 如果這個時候a不是b的字首 那麼所有字典序小於a的並且不是a的字首的字串均不是b的字首 所以我們先對串排序,然後直接dp f i j 表示1 j號串裡取i個 j號必須取 的所喲方案數 f 0 0 0 include defin...

題解 字首單詞

一組單詞是安全的,當且僅當不存在乙個單詞是另乙個單詞的字首,這樣才能保證資料不容易被誤解。現在你手上有乙個單詞集合,你需要計算有多少個子集是安全的。注意空集永遠是安全的。首先 設dp i 表示前i個集合以i結尾的集合數 核心 dp i dp j 1 j但是問題在於與j不互為字首的單詞不一定與i不互為...