usaco3.1 contact

博客内容讲述了USACO3.1 Contact问题的解决方案,通过统计01串中长度从A到B子串的出现次数,找出最频繁的N个子串。博主分享了朴素的AC代码,并介绍了两种不同的思路:一是边读入边计算,利用极限掩码和位移操作更新子串;二是利用二叉树进行存储和统计。博客强调了输入输出的细节和代码效率。
摘要由CSDN通过智能技术生成

题目链接


题意:

给一个01串s,统计其中长度从A到B的各个子串出现的次数,输出频率最高的N个


十分朴素且无脑地过了...还很快

对于A到B的每个长度len,取出s中所有该长度的串,转化为二进制x,++cnt[len][x],

再作为node放到ans数组中,node有三个成员,val, len, cnt(值,长度,出现次数)

对ans排序,再输出即可

再就是,输入输出稍微注意一点


AC代码如下:

/*
PROB:contact
LANG:C++
ID:fan_0111
*/
#include <cstdio>
#include <algorithm>
struct node { int len, cnt, val; }ans[10010];
bool cmp(node a, node b) {
    if (a.cnt == b.cnt) {
        if (a.len == b.len) return a.val < b.val;
        return a.len < b.len;
    }
    return a.cnt > b.cnt;
}
int cnt[13][10010];
char ch[200020];
void print(int len, int x) {
//    printf("\n%d %d\n", len, x);
    char ret[len + 1] = "";
    ret[len] = '\0';
    int i = len - 1;
    while (x) {
        ret[i--] = x % 2 + '0';
        x /= 2;
    }
    for (int j = i; j >= 0; --j) ret[j] = '0';
    printf("%s", ret);
}
using namespace std;
int main() {
    freopen("contact.in", "r", stdin);
    freopen("contact.out", "w", stdout);
    int a, b, n;
    scanf("%d%d%d\n", &a, &b, &n);
    int len = 0;
    while (scanf("%c", &ch[len]) != EOF) {
        if (ch[len] == '\n') continue;
        ++len;
    }
    b = len < b ? len : b;
//    printf("%c\n", ch[0]);
//    printf("%d\n", len);
//    ch[len++] = '\0';
//    printf("%s\n", ch);
    for (int i = a; i <= b; ++i) {
        for (int s = 0; s <= len - i; ++s) {
            int t = s + i, x(0);
            for (int k = s; k < t; ++k) {
                x = x * 2 + ch[k] - '0';
            }
            ++cnt[i][x];
        }
    }
    int tot(0);
    for (int i = a; i <= b; ++i) {
        for (int j = 0; j < (1 << i); ++j) {
            if (cnt[i][j]) {
                ans[tot].len = i; ans[tot].cnt = cnt[i][j]; ans[tot++].val = j;
            }
        }
    }
    sort(ans, ans + tot, cmp);
//    for (int i = 0; i < tot; ++i) {
//        printf("%d %d %d\n", ans[i].len, ans[i].val, ans[i].cnt);
//    }
    printf("%d\n", ans[0].cnt);
    print(ans[0].len, ans[0].val);
    int prev = ans[0].cnt, ln = 1, N(1);
    for (int i = 1; i < tot; ++i) {
        if (ans[i].cnt != prev) {
            ++N; if (N == n + 1) break;
            printf("\n%d\n", ans[i].cnt);
            print(ans[i].len, ans[i].val);
            prev = ans[i].cnt;
            ln = 1;
            continue;
        }
        if (ln % 6 == 0) printf("\n"); else printf(" ");
        print(ans[i].len, ans[i].val);
        ++ln;
    }
    printf("\n");
    return 0;
}


后来看题解

看到两种想法

1. 解析原串获取子串时,

  1. 边读入边计算。 设置极限掩码为limit=(1<<(B))-1; //2的B此次方-1  
  2. 每读入一个二进制数0或1,令unsigned int数字串str=((str<<1)+c) & limit;  
  3. 然后扫描str,从末尾向前扫描i=(A到B)位,把所得的数字串t最高位添加1,以区别有前导0的串,例如001和01,添加后变为1001和101 
  4.  
  5. mask=(1<<i)-1; 
  6. mask2=mask+1; 
  7. t=(S & mask) | mask2; 
  8. 用哈希表记录t的频率 hash[t]++; 

(出自 http://blog.csdn.net/damonhao/article/details/19975423


2. 存储时,使用二叉树

#define  LEF(x) (2*(x)+1)
#define  RIG(x) (2*(x)+2)

     int i, j, idx;
     for(i = 0; i < len; i++)
     {
	  idx = 0;
	  for(j = 0; j < b; j++)
	       if(i+j < len)
	       {
		    if(s[i+j] == '0')
			 idx = LEF(idx);
		    else
			 idx = RIG(idx);
 
		    trie[idx]++;
	       }
     }
(出自  http://www.nocow.cn/index.php/Code:USACO/contact/C

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值