1/20集训一 STL B.(用set/map记录元素是否重复出现)Pearls in a Row

1/20集训一 STL

B.(用set/map记录元素是否重复出现)Pearls in a Row

There are n pearls in a row. Let’s enumerate them with integers from 1 to n from the left to the right. The pearl number i has the type ai.

Let’s call a sequence of consecutive pearls a segment. Let’s call a segment good if it contains two pearls of the same type.

Split the row of the pearls to the maximal number of good segments. Note that each pearl should appear in exactly one segment of the partition.

As input/output can reach huge size it is recommended to use fast input/output methods: for example, prefer to use scanf/printf instead of cin/cout in C++, prefer to use BufferedReader/PrintWriter instead of Scanner/System.out in Java.

Input
The first line contains integer n (1 ≤ n ≤ 3·105) — the number of pearls in a row.

The second line contains n integers ai (1 ≤ ai ≤ 109) – the type of the i-th pearl.

Output
On the first line print integer k — the maximal number of segments in a partition of the row.
Each of the next k lines should contain two integers lj, rj (1 ≤ lj ≤ rj ≤ n) — the number of the leftmost and the rightmost pearls in the j-th segment.
Note you should print the correct partition of the row of the pearls, so each pearl should be in exactly one segment and all segments should contain two pearls of the same type.
If there are several optimal solutions print any of them. You can print the segments in any order.
If there are no correct partitions of the row print the number “-1”.
Example
Input
5
1 2 3 4 1
Output
1
1 5
Input
5
1 2 3 4 5
Output
-1
Input
7
1 2 1 3 1 2 1
Output
2
1 3
4 7

题意:
给定一个整数列,如果其子串中包含两个相同元素, 则这个子串为good segments,问最多可以分成几个good, 并输出分割的首尾端点。

思路:
将每一个字符和它所在位置储存在map中,如果这个字符在map中出现过,那么就说明这是一个good,用数组记录下其首尾的坐标。对于最后一个子串要特殊处理, 如果最后一个子串不能成为good, 而其前面存在分割子串,那么就将这个子串和它前面的子串合并为一个子串,否则说明不能分割。

#include<bits/stdc++.h>
using namespace std;
struct Node{
    int x,y;
};
int main(){
    int n;
    cin>>n;
    Node ans[300005];
    map<int,int>Q;
    int num=0,s=1;
    int a;
    for(int i=1;i<=n;i++){
        cin>>a;
        if(!Q[a])
            Q[a]=i;
        else{
            ans[num].x=s;
            ans[num].y=i;
            num++;
            s=i+1;
            Q.clear();
        }
    }
    if(num==0)
        cout<< -1 <<endl;
    else{
        if(ans[num-1].y!=n)
            ans[num-1].y=n;
        cout<<num<<endl;
        for(int i=0;i<num;i++){
            cout<<ans[i].x<<" "<<ans[i].y<<endl;
        }
    }
    return 0;
}

这种题用set更多一些(思路一样)

#include <cstdio>
#include <set>
using namespace std;
const int maxn=300005;
int ans[maxn];
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        set<int> se;
        int l=1,cnt=0;
        int tot=0;
        for(int i=1; i<=n; i++)
        {
            int x;
            scanf("%d",&x);
            if(!se.count(x))se.insert(x);
            else
            {
                ans[tot++]=l;
                ans[tot++]=i;
                l=i+1;
                cnt++;
                se.clear();
            }
        }
        if(ans[tot-1]<n)ans[tot-1]=n;
        if(tot)
        {
            printf("%d\n",cnt);
            for(int i=0; i<tot; i+=2)
                printf("%d%d\n",ans[i],ans[i+1]);
        }
        else printf("-1\n");
    }
}

错误代码如下

#include <iostream>
#include <set>
#include <stdio.h>

using namespace std;

const int maxn = 3e5 + 5;
int s[maxn], e[maxn];

int main()
{
    int n, num = 0, flag = 0, cur, f = 0;
    set<int> p;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
    {
        scanf("%d", &cur);
        if(flag == 0)
        {
            s[++num] = i;
            p.insert(cur);
            flag = 1;
        }
        else
        {
            if(p.count(cur))
            {
                f = 1;
                e[num] = i;
                flag = 0;
                p.clear();
            }
            else
                p.insert(cur);
        }
    }
    e[num] = n;
    if(f)
    {
        printf("%d\n", num);
        for(int i = 1; i <= num; i++)
            printf("%d %d\n", s[i], e[i]);
    }
    else
        printf("-1\n");
    return 0;
}

之前老是死在test8上,因为我每一次不管有没有重复数字出现,都会先把s[ ]更新,同时num++,然后再看能不能把e[ ]给填补上。这样就会导致一个问题,如果有下面这样的数列 1 2 1 3 2 3 0,那么我的数列会先把121划为第一组(s【1】=1,e【1】= 3),
然后323 划为第二组(s【2】=4,e【2】= 6),此时s【3】更新为7,但是最后并没有相同数字再出现,那么我又想把0划到上面最后一组去,于是我强行使最后一组的e【num】=n,然而由于s【】和num更新了,此时的num并不是最后一组的,而是多出来的一组,于是我使得e【3】= 7了(s【3】=7,e【3】= 7),本意是(s【2】=4,e【2】= 7)。
所以说先更新s【】,再更新e【】,是有弊端的,最好找到相同数字之后,一起更新。

于是有正确代码:

#include <iostream>
#include <set>
#include <stdio.h>

using namespace std;

const int maxn = 3e5 + 5;
int s[maxn], e[maxn];

int main()
{
    int n, num = 0, flag = 0, st;
    set<int> p;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
    {
        int cur;
        scanf("%d", &cur);
        if(flag == 0)
        {
            st = i;
            p.insert(cur);
            flag = 1;
        }
        else
        {
            if(p.count(cur))
            {
                num++;
                s[num] = st;
                e[num] = i;
                flag = 0;
                p.clear();
            }
            else
                p.insert(cur);
        }
    }
    e[num] = n;
    if(num)
    {
        printf("%d\n", num);
        for(int i = 1; i <= num; i++)
            printf("%d %d\n", s[i], e[i]);
    }
    else
        printf("-1\n");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值