51Nod1526 分配笔名

分析

在trie树上贪心,将所有串加入trie树中,在深度较深的地方匹配会更优。

由于只需要知道最后的总质量,所以直接取每个点的子树中最大的匹配即可

复杂度\(O(\sum len)\)

加串的时候把路径上\(val\)加\(1\),查询串的时候把沿途\(val\)减\(1\),\(ans\)为减去的\(1\)的个数。

注意\(val\)为\(0\)的情况,如果后面有\(val\)为大于等于\(1\)的点,那么说明这次查询的串比以前查询的串贡献的答案更优,那么就直接加上后面那些\(val\)大于等于\(1\)的点的个数,而不用撤销操作。因为如此计算相当于自动撤销了之前的次优解。不存在次优解到另一分叉而使此串查询路径上\(val\)为\(0\)的情况,因为如果到另一分叉则此串查询路径上至少加了两个串。

代码

#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<ctime>
#include<iostream>
#include<string>
#include<vector>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
#include<complex>
#pragma GCC optimize ("O0")
using namespace std;
template<class T> inline T read(T&x){
    T data=0;
    int w=1;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch==‘-‘)
            w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
        data=10*data+ch-‘0‘,ch=getchar();
    return x=data*w;
}
typedef long long ll;
const int INF=0x7fffffff;

struct Trie
{
    static const int SigmaSize=26;
    int index(char c)
    {
        return c-‘a‘;
    }
    int ch[800010][26];
    int val[800010];
    int tcnt;
    void insert(char*s)
    {
        int f=0;
        while(*s)
        {
            int c=index(*s);
            if(!ch[f][c])
                ch[f][c]=++tcnt;
            f=ch[f][c];
//          cerr<<"*s="<<*s<<" f="<<f<<endl;
            ++val[f];
            ++s;
        }
    }
    int query(char*s)
    {
        int f=0,ans=0;
        while(*s)
        {
            int c=index(*s);
            if(!ch[f][c])
                return ans;
            f=ch[f][c];
            if(val[f]>0)
                ++ans,--val[f];
//          else
//              return ans; // edit 2 : 不能直接return,相当于撤销之前的操作
            ++s;
        }
        return ans; // edit 1 : 保证完全匹配有返回值
    }
}T;

char s[800010];

int main()
{
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    int n;
    read(n);
    for(int i=1;i<=n;++i)
    {
        scanf("%s",s);
        T.insert(s);
    }
    int ans=0;
    for(int i=1;i<=n;++i)
    {
        scanf("%s",s);
        ans+=T.query(s);
    }
    printf("%d\n",ans);
//  fclose(stdin);
//  fclose(stdout);
    return 0;
}

原文地址:https://www.cnblogs.com/autoint/p/9532179.html

时间: 2024-11-08 23:09:23

51Nod1526 分配笔名的相关文章

51Nod 1526 分配 笔名

1526 分配 笔名 题目来源: CodeForces 基准时间限制:1 秒 空间限制:131072 KB 分值: 320 难度:7级算法题  收藏  关注 班里有n个同学.老师为他们选了n个笔名.现在要把这些笔名分配给每一个同学,每一个同学分配到一个笔名,每一个笔名必须分配给某个同学.现在定义笔名和真名之间的相关度是他们之间的最长公共前缀.设笔名为a,真名为b,则他们之间的相关度为lcp(a,b).那么我们就可以得到匹配的质量是每一个同学笔名和真名之间相关度的和. 现在要求分配笔名,使得匹配质

1526 分配笔名(未完成存档)

#include <cstring> #include <cstdio> #define N 1000000 char real[N]; int f[N],pos,len,n,trie[N][26],exict[N],f[N],siz=1; inline void ins() { int len=strlen(real); for(int i=0;i<len;++i) { int id=real[i]-'a'; if(!trie[p][id]) trie[p][id]=++s

【c/c++】内存分配大小

测试平台:linux 32位系统 用sizeof()运算符计算分配空间大小.单位:字节 1. 数组名与变量名的区别 int main() { char q[] = "hello"; cout << "q:" << sizeof(q) << endl; char *mq = q; cout << "mq:" << sizeof(mq) << endl; const char *

操作系统: 最佳适配算法和邻近适配算法的模拟实现(内存分配算法)

实现动态分区的分配算法. (1) 最佳适配算法:选择内存空闲块中最适合进程大小的块分配. (2) 邻近适配算法:从上一次分配的地址开始查找符合要求的块,所查找到的第一个满足要求的空闲块就分配给进程. 模拟添加进程的时候,假定内存是一块完整的空闲区,对于算法(1)来说,分配的时候遍历所有的空闲内存块,找出其中最适合的一块,注意此时内存分区的总块数可能已经发生了变化: 对于算法(2)来说,则需要从上次分配的内存块(使用变量记录即可)接着向下找到第一个满足条件的块即可,内存分区的总块可能也已经发生了变

JAVA中堆栈和内存分配

(一).栈.堆 1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制.2. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(字符串常量对象存放在常量池中.)3. 堆:存放所有new出来的对象.4. 静态域(属于方法区) :存放静态成员(static定义的)5. 常量池 (属于方法区):存放字符串常量和基本类型常量(public static final).6. 非RAM存储:硬盘等永久存储空间这里我们主要关心栈

JAVA内存区域及使用分配

JAVA虚拟机运行时会将JVM使用的内存划分为不同的区域,每个区域负责不同的功能,以及各个区域的创建,销毁都各不相同. 下图是JVM运行时内存数据区的划分, 图1.JVM运行时数据区 1.程序计数器 每个线程都拥有一个独立的程序计数器,用于记录当前线程所要执行的字节码指令,该类内存区域为"线程私有"内存. 2.虚拟机栈 该区域也有人称为栈内存(对应java堆内存),这个叫法不完全正确,但可以便于理解. 该区域也是线程私有的,并且与线程的生命周期相同. 主要负责方法执行的内存部分,在每个

实验四 内存的分配与回收

#include <stdio.h> #include <malloc.h> #include <process.h> #include <string.h> #define minisize 1 typedef struct freeTable { char proID[6]; int startAddr; /*空闲区起始地址*/ int length; /*空闲区长度,单位为字节*/ int flag; /*空闲区表登记栏标志,用"0"

nginx之upstream集中分配方式

一.分配方式 1.轮询方式(默认) upstream realserver {     server 192.168.1.1;     server 192.168.1.2; } 每一个请求会按照时间顺序分配到后端不同的服务器上,假如有一台服务器宕机,则会自动剔除该服务器. 2.weight权重 upstream realserver {         server 192.168.1.1 weight=5;         server 192.168.1.2 weight=8; } 根据后

Lua2.4 内存分配 mem.c

先看看头文件里的定义,定义了一些出错信息,几个内存管理接口,和宏.几个宏的定义也是用到了内存分配的几个接口.newvector 在之前的词法分析里已经出现了.几个接口,看函数名字比较容易看出来它的作用.下面看一下它们各自的实现: void luaI_free (void *block) {   if (block)   {     *((int *)block) = -1;  /* to catch errors */     free(block);   } } 释放内存.设置 -1 的那个地