zoukankan      html  css  js  c++  java
  • 【ybtoj】【单调队列】写博客

    题意

    小泽发了一篇博客,由 n 个小写英文字母组成,由于包含违禁词,被自动隐藏。
    具体地,违禁词有 m 个,分别为 T1,T2,…,Tm 。
    小泽发现,只要博客中,连续地包含了其中违禁词,那么博客就会被自动隐藏。换言之,对于任意 1≤i≤m , Ti 都不能是最终发表的博客 S 的子串。
    于是小泽决定在原来的博客 S 上把一部分字母替换成空格,使得它不再包含违禁词。如果她把第 i 个字母替换成空格,与之相邻的两个字母将不会连续,但是整篇博客的价值会减少 ai 。
    小泽想要知道,如何替换可以得到一篇不会被自动隐藏的博客,而价值的减少量最少。请你帮她回答这个问题。
    1≤n=|S|≤2×105 , 1≤m≤10 , 1≤|Ti|≤2×105 , 0≤ai≤1000.

    题解

    综合性强的一道好题。
    根据数据范围可知 dp 数组应该只有一维状态,那么只能设 (dp_i) 表示处理好前 (i) 的字母减少的最小价值,而且第 (i) 个位置必须改为空格。
    可以想到先预处理出违禁串在原串出现的位置,这个可以用 KMP 解决。
    把每一个违禁串的位置记为 ([l,r]),记录 (pre_r=l).
    那么对于每一个 (dp_i),有 (dp_i)=(dp_j)+(a_i).
    考虑 (j) 的取值范围,是要将 (i-1) 之前的位置处理好,那么最后一个需要覆盖的区间就是 ([pre_{i-1},i-1])
    (pre_i) 虽然实际意义上不一定单调不降,但是实际上要维护单调不降,才能转移 dp,同时也能进行单调队列优化。
    初始的时候队列里要加入一个“0”元素,保证从“0”转移的情况。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int INF = 0x3f3f3f3f,N = 2e5+10;
    inline ll read()
    {
    	ll ret=0;char ch=' ',c=getchar();
    	while(!(c>='0'&&c<='9')) ch=c,c=getchar();
    	while(c>='0'&&c<='9') ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
    	return ch=='-'?-ret:ret;
    }
    int n,m,nxt[N],pre[N];
    char s[N],c[11][N];
    int a[N];
    int dp[N],q[N],l=1,r;
    
    void pretreat(int op)
    {
    	int j=0,len=strlen(c[op]+1);
    	for(int i=1;i<len;i++) 
    	{
    		while(j&&c[op][j+1]!=c[op][i+1]) j=nxt[j];
    		if(c[op][j+1]==c[op][i+1]) j++;
    		nxt[i+1]=j;
    	}
    }
    void kmp(int op)
    {
    	int j=0,len=strlen(s+1),lenc=strlen(c[op]+1);
    	for(int i=0;i<len;i++)
    	{
    		pre[i+1]=max(pre[i],pre[i+1]);
    		while(j&&c[op][j+1]!=s[i+1]) j=nxt[j];
    		if(c[op][j+1]==s[i+1]) j++;
    		if(j==lenc) pre[i+1]=max(pre[i+1],i+1-lenc+1),j=nxt[j];
    		//我吐了,lenc打成len...调了好久 
    		//注意细节,pre[i+1]的位置刚好是一个违规词 
    	}
    }
    int main()
    {
    	n=read(),m=read();
    	scanf("%s",s+1);
    	for(int i=1;i<=n;i++) a[i]=read();
    	for(int i=1;i<=m;i++)
    	{
    		scanf("%s",c[i]+1);
    		pretreat(i);
    		kmp(i);
    	}
    	n++;
    	l=1,r=0;
    	for(int i=0;i<=n;i++)
    	{
    		while(l<=r&&q[l]<pre[i-1]) l++;
    		dp[i]=dp[q[l]]+a[i];
    		while(l<=r&&dp[q[r]]>=dp[i]) r--;
    		q[++r]=i;
    	}
    	printf("%d",dp[n]);
    	return 0;
    }
    
  • 相关阅读:
    MySQL性能优化的最佳20+条经验
    最新 DEDECMS SQL 注入 0day
    PHP开发程序应该注意的42个优化准则
    PHP面向对象(OOP)编程入门教程
    【PHP面向对象(OOP)编程入门教程】23.自动加载类 __autoload()函数
    【PHP面向对象(OOP)编程入门教程】22.把对象串行化serialize()方法,__sleep()方法,__wakeup()方法
    【PHP面向对象(OOP)编程入门教程】21.多态的应用
    家族企业的常青之道——leo鉴书68
    Windows消息传递机制具体解释
    Computer Vision and Machine Learning Competitions
  • 原文地址:https://www.cnblogs.com/conprour/p/15294331.html
Copyright © 2011-2022 走看看