Codeforces Round #732 (Div. 2)B. AquaMoon and Stolen String

49 篇文章 0 订阅
13 篇文章 0 订阅

题目大意

给出 n n n个长度为 m m m的字符串,其中 n n n奇数
现在进行一波操作,
将这 n n n个字符串两两配对,其中有一个字符串无法配对,不妨记作 s 0 s_0 s0
每一对字符串可以交换任意个对应位置的字母。
例如,字符串abcdezyxwv,它们交换第一位,第三位之后就变为了zbxdeaycwv

现在再给出 n − 1 n-1 n1个长度为 m m m的字符串,是经过一波操作之后的字符串(但并不给出具体操作),求 s 0 s_0 s0 n n n个长度为 m m m的字符串中的哪一个。

时间限制

1s

数据范围

n ≤ 1 0 5 n \le 10^5 n105
m ≤ 1 0 5 m \le 10^5 m105
n × m ≤ 1 0 5 n \times m \le 10^5 n×m105

题解

判断字符串是否相等,最常用的一个办法就是hash
不难发现,一对字符串,无论它们如何交换对应位置的字母,其的哈希值的和都是不变的。
因此,经过操作后的 n − 1 n-1 n1个串哈希值的和,应该等于原来 n n n个串中,对应的那 n − 1 n-1 n1个串哈希值的和。
如果知道了 n n n个串的哈希值的和,那么 s 0 s_0 s0的哈希值也就十分显然了。

对应哈希冲突的问题,
如果担心被hack,可以多选几个模数,变成双哈希,多哈希即可。

Code

//#pragma GCC optimize (2)
//#pragma G++ optimize (2)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#include <vector>
#include <queue>
#define G getchar
#define ll long long
using namespace std;

int read()
{
    char ch;
    for(ch = G();(ch < '0' || ch > '9') && ch != '-';ch = G());
    int n = 0 , w;
    if (ch == '-')
    {
        w = -1;
        ch = G();
    } else w = 1;
    for(;'0' <= ch && ch <= '9';ch = G())n = (n<<1)+(n<<3)+ch-48;
    return n * w;
}

const int N = 100005;
const int mo = 998244353;

struct node
{
    string s;
    ll hh;
    bool operator < (const node & n)const
    {
        return hh < n.hh;
    }
}a[N] , tmp;

int k[12] = {27 , 33 , 66 , 88 , 114 , 127 , 188 , 12341 , 999 , 14323 , 75754 , 39391};
int n , m;
bool bz;
ll sum , t , S;
char ch;

int main()
{
    //freopen("b.in","r",stdin);
    //freopen("b.out","w",stdout);
    
    for (int T = read() ; T ; T--)
    {
        n = read();
        m = read();
        for (int i = 1 ; i <= n ; i++)
            cin>>a[i].s;
            //scanf("%s" , a[i].s);

        for (int i = 0 ; i < 12; i++)
        {
            for (int j = 1 ; j <= n ; j++)
            {
                a[j].hh = 0;
                for (int tt = 0 ; tt < m ; tt ++)
                    a[j].hh =(a[j].hh * k[i] + a[j].s[tt] - 'a') % mo;
            }
            sort(a + 1 , a + 1 + n);
            bz = 1;
            for (int j = 1 ; j < n ; j++)
                if (a[j].hh == a[j + 1].hh)
                {
                    bz = 0;
                    continue;
                }

            if (bz || i == 11)
            {
                sum = 0;
                ch = G();
                for (int j = 1 ; j < n ; j++)
                {
                    t = 0;
                    for (; ch < 'a' || ch > 'z';ch = G());
                    for (int tt = 0 ; tt < m ; tt ++)
                    {
                        t = (t * k[i] + ch - 'a') % mo;
                        ch = G();
                    }
                    sum = (sum + t) % mo;
                }

                S = 0;
                for (int j = 1 ; j <= n ; j++)
                    S = (S + a[j].hh) % mo;

                tmp.hh = (S - sum + mo) % mo;
                //printf("%s\n", a[lower_bound(a + 1 , a + 1 + n , tmp) - a - 1].s);
                cout<<a[lower_bound(a + 1 , a + 1 + n , tmp) - a].s<<endl;
                break;
            }
        }
    }
    fflush(stdout);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值