题解 CF919D 【Substring】

ZolaWatle

2020-08-18 19:40:59

Solution

# CF919D题解 容易发现,对于每一个点都只有一个状态,而且对于整张图最多只有26个状态(即所有小写字母的总数)。 再看数据范围,$ n $ 的范围 $ 3e5 $,搜索肯定是不行的。在状态数量那么小的情况下,状态是很好表示的。为了方便数组存储,可以用 $ idx_i = tmp - 'a\ ' + 1 $ 表示($ tmp $ 是 $ i $ 号节点的字母)。这样一来,$ a - z $ 可以转化为 $ 1 - 26 $ 这26个数字。 我们考虑使用**DP**:开一个数组 $ dp_{i,j} $ 表示当走到 $ i $ 点时字母 $ j $ 的最大出现次数。当然,经过了前面的分析,$ j $ 是使用数字表示的。 应当在读入每个点的字母时初始化 dp 数组,使得路径 “ $ x -> x $ ” 上字母 $ idx_x $ 的出现次数为 $ 1 $。 由于这张图是**有向图**,为了满足动态规划的**无后效性**,考虑使用**拓扑排序**进行dp。 设当前操作是从节点 $ x $ 走向节点 $ y $,我们分两种情况考虑状态转移方程: - 若 $ idx_y = j $,即节点 $ y $ 的字母和现在扫描到的字母相同,那么走到 $ y $ 后,这个字母的出现次数要加一。即:$ dp_{y,j} = max $ { $ dp_{y,j} , dp_{x,j} + 1 $ } 。 - 同理,若字母不同,次数不必加一。即:$ dp_{y,j} = max $ { $ dp_{y,j} , dp_{x,j} $ } 。 在进行拓扑排序时进行状态转移即可。 当然,还要考虑输出 $ -1 $ 的情况。可在拓扑排序过程中开一个计数器 $ sum $,每有一个元素出队就加一,最后判断它是否小于 $ n $,如果小于,则说明有环,输出 $ -1 $ 就行了。 完整代码如下(详情见注释): ```cpp #include <iostream> #include <cstdio> #include <cstring> #include <queue> #include <vector> #define re register int using namespace std; const int MAXN=3e5+1; int n,m,ans,sum,idx[MAXN],in[MAXN],dp[MAXN][27]; vector <int> E[MAXN]; queue <int> q; inline int read() { int x=0,f=1;char ch=getchar(); while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while (isdigit(ch)){x=x*10+ch-48;ch=getchar();} return x*f; } int main() { n=read(),m=read(); for(re i=1;i<=n;i++) { char tmp=getchar(); //这样读入速度更快 idx[i]=tmp-'a'+1; //也可以写tmp-95 dp[i][idx[i]]++; //初始化 } for(re i=1;i<=m;i++) { re x=read(),y=read(); E[x].push_back(y); //用vector存边 in[y]++; //统计入度 } for(re i=1;i<=n;i++) if(!in[i]) q.push(i); while(!q.empty()) { re x=q.front(); q.pop(); in[x]=-1; //删点 sum++; for(re i=0;i<E[x].size();i++) { re y=E[x][i]; for(re j=1;j<=26;j++) { if(idx[y]==j) dp[y][j]=max(dp[y][j],dp[x][j]+1); else dp[y][j]=max(dp[y][j],dp[x][j]); }//状态转移 in[y]--; if(!in[y]) q.push(y); } }//拓扑基本操作 if(sum<n) { printf("-1"); return 0; }//The value can be arbitrarily large for(re i=1;i<=n;i++) for(re j=1;j<=26;j++) ans=max(ans,dp[i][j]); //由于这条路径可以在任意点出发,任意点结束,因此在dp完毕后还要像这样计算答案 printf("%d",ans); return 0; } ``` 谢谢观看~