首页 > 试题广场 >

宝石手串

[编程题]宝石手串
  • 热度指数:9013 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 512M,其他语言1024M
  • 算法知识视频讲解
\hspace{15pt}小红有一个 n 颗宝石构成的环形宝石手串,即第一和最后一宝石相连,其中第 i 个宝石的属性为 s_i ;若两个宝石的属性相同,那么这两个宝石会相互排斥,导致断开。
\hspace{15pt}小红可以从手串中摘掉一些宝石,每次摘掉后,这个宝石左右的两个宝石会相接,手串依旧是环形。
\hspace{15pt}小红想要破坏这个手串。她想要知道,最少还需要摘掉多少个宝石才会导致手串断开。特别的,当手串上剩余的宝石数量恰好为 2 而依旧没能断开时,视为破坏失败,直接输出 -1

输入描述:
\hspace{15pt}每个测试文件均包含多组测试数据。第一行输入一个整数 T\left(1\leqq T\leqq 100\right) 代表数据组数,每组测试数据描述如下:

\hspace{15pt}第一行输入一个整数 n \left( 2 \leqq n \leqq 10^5 \right) 代表手串初始的宝石数量。
\hspace{15pt}第二行输入一个长度为 n 、仅由小写字母构成的字符串,代表手串上每个宝石的属性。

\hspace{15pt}除此之外,保证单个测试文件的 n 之和不超过 10^5


输出描述:
\hspace{15pt}对于每一组测试数据,如果手环无法破坏,直接输出 -1 ;否则,在一行上输出一个整数,代表手串断开需要的最少操作次数。
示例1

输入

2
2
ac
3
aac

输出

-1
0
T = int(input())
for i in range(T):
    n = int(input())
    s = input()
    ss = s+s
    if len(s)==len(set(s)):
        print(-1)
    else:
        flag = False
        for j in range(1,len(s)+1):
            for k in range(len(s)):
                if ss[k]==ss[k+j]:
                    print(j-1)
                    flag = True
                    break
            if flag:
                break
发表于 2025-03-19 23:35:44 回复(0)
/* Map<Character,List<Integer>> 想过 但居然真的可以实现!!! 感谢D老师
一个一个比较也可以 但是复杂度是O(宝石手串数量*单个手串数量中宝石数的2次方) 在用例5/20会过不去  时间超了 (单个宝石手串有50000个宝石 离谱!!)  但是用以下代码 复杂度是O(宝石手串数量*单个手串数量中宝石数)
*/

import java.util.Scanner;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        int q = in.nextInt();
        in.nextLine();
        while(q-- >0) {
            int w = in.nextInt();
            in.nextLine();
            String b = in.nextLine();
            if(w==2){
                System.out.println(-1);
                continue;
            }
            Map<Character,List<Integer>> c = new HashMap<>();
            for(int i = 0;i<w;i++){
                char d = b.charAt(i);
                if(!c.containsKey(d)){
                    c.put(d,new ArrayList<>());
                }
                c.get(d).add(i);
            }//统计了所有有重复字符的位置 并且用列表存起来了
            //剔除2种情况 1.手串长度为2已经剔除了 2.没有重复的字符 所有都唯一存在 输出-1 这是list的列表长度为1
            int min = Integer.MAX_VALUE;
            for(List<Integer> e:c.values()){
                if(e.size()<2){
                    continue;
                }
                Collections.sort(e);
                List<Integer> f = new ArrayList<>(e);
                f.add(f.get(0)+w);
                for(int i = 0;i<f.size()-1;i++){
                    int x = f.get(i);
                    int y = f.get(i+1);
                    int z = y-x;
                    min = Math.min(min,Math.min(z,w-z));
                }
            }
            if(min == Integer.MAX_VALUE){System.out.println(-1);
            }else {
                System.out.println(min-1);//输出为间隔字符 这里min要-1
            }
        }
    }
}

发表于 2025-03-17 16:05:35 回复(0)
#include <algorithm>
#include <climits>
#include <cstdint>
#include <iostream>
#include <set>
#include <vector>
using namespace std;

int main() {

    int n;
    cin >> n;

    while (n--) {
        int cnt;
        string str;
        cin >> cnt;
        cin >> str;

        bool hasConnect = false;
        for ( int i = 0; i < cnt; i++ ) {
            if ( str[i] == str[ (i + 1) % cnt ] ) {
                hasConnect = true;
                break;
            }
        }

        if ( hasConnect ) {
            cout << 0 << endl;
            continue;
        }

        if ( cnt == 2 ) {
            cout << -1 << endl;
            continue;
        }

        int min_gap = INT_MAX;
        for ( char c = 'a'; c <= 'z'; c++ ) {
            vector<int> vec;
            for ( int i = 0; i < cnt; i++ ) {
                if ( str[i] == c ) {
                    vec.push_back(i);
                }
            }
            if ( vec.size() < 2 ) {
                continue;;
            }
            for ( int i = 0; i < vec.size() - 1; i++ ) {
                if ( vec[i + 1] - vec[i] - 1 < min_gap ) {
                    min_gap = vec[i + 1] - vec[i] - 1;
                }
            }
            //环形处理
            int nu = ( cnt - vec.back() ) + vec.front() - 1;
            min_gap = min( nu, min_gap );

        }
        if ( min_gap == INT_MAX ) {
            cout << -1 << endl;
        } else {
            cout << min_gap << endl;
        }

    }
}
// 64 位输出请用 printf("%lld")

发表于 2025-05-03 01:04:42 回复(0)
def baoshi(s):
    c = 1
    for char in s:
        if s.count(char) >= 2:
            c = 0
    if c:
        return -1
    huan_s = s + s
    break_c = []
    for i in range(len(huan_s)):
        for j in range(i+1,len(huan_s)):
            if huan_s[i] == huan_s[j]:
                c = j - i - 1
                break_c.append(c)
    return min(break_c)

n = int(input())
for i in range(n):
    k = int(input())
    shoucuan = str(input())
    print(baoshi(shoucuan))
#运行超时暂时懒得改了

发表于 2025-04-10 20:20:13 回复(2)
求相同字符的最短间隔, 可以遍历, 维护一个字典, 键是字符, 值分别记录当前字符的下标和最短距离的最小值
发表于 2025-03-06 23:40:52 回复(0)
主要是使用一个map来存储宝石中各种类型的宝石位置,同一类宝石例如c求出最短的距离即可。例如 3-1 = 2
发表于 2026-02-01 16:07:37 回复(0)
n=int(input())
for i in range(n):
    num=input()
    s=input()*2
    if len(s)==4:
        print(-1)
    else:
        list1=[]
        for j in range(len(s)//2):
            for k in range(j+1,j+len(s)//2):
                if s[j]==s[k]:
                    list1.append(k-j)
                    break
        if len(list1)==0:
            print(-1)
        else:
            print(min(list1)-1)

发表于 2026-01-22 17:18:52 回复(0)
#include <climits>
#include <iostream>
#include <vector>
using namespace std;
 
int main() {
    int n;
    cin >> n;
    vector<pair<int, string>> vec(n);//store input data
    int num;
    string str;
    for (int i = 0; i < n; ++i) { //赋值给vec
        cin >> num;
        cin >> str;
        vec[i].first = num;
        vec[i].second = str;
    }
 
    for (int i = 0; i < n; ++i) {
        int min_gap = INT_MAX;
        for (char c = 'a'; c <= 'z'; ++c) { //本来想暴力破解,奈何运行时间超出要求,那就遍历26个字母了
            char loc = c;
            vector<int> subscript; //用于存放字母的下标
            for (int j = 0; j < vec[i].first; ++j) {
                if ( loc == vec[i].second[j])
                    subscript.push_back(j); //存入字母下标
            }
            if (subscript.size() == 0 || subscript.size() == 1 )
                continue; //无该字母或只有一个该字母,无须执行之后操作
            for (int k = 0; k < subscript.size() - 1; ++k) {
                int a = subscript[k + 1] - subscript[k] - 1;
                min_gap = min(min_gap, a );
            }
            int b = subscript[0] + vec[i].first - subscript[subscript.size() - 1] - 1;//环形字母间隔
            if (b < min_gap)
                min_gap = b;
            if (min_gap == 0) //如果存在两相同字母紧挨,则排斥,无须之后继续遍历当下字符串
                break;
        }
        if (min_gap == INT_MAX)
            cout << -1 << endl;
        else
            cout << min_gap << endl;
    }
    return 0;
}

发表于 2026-01-09 17:35:43 回复(0)
T=int(input())
for i in range(T):
    n=int(input())
    s=input()
    s_dict={}
    count=len(s)
    if len(s)==len(set(s)):
        print('-1')
    else:
        for idx,char in enumerate(s):
            if char in s_dict:
                s_dict[char].append(idx)
            else:
                s_dict[char]=[idx]
        for item,value in s_dict.items():
            if len(value)>=2:
                count=min(count,min(value[i]-value[i-1] for i in range(1,len(value)))-1,len(s)-value[-1]+value[0]-1)
        print(count)
我的思路应该是最易懂的(肯定能进一步简化)
发表于 2026-01-01 12:56:01 回复(0)
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别

        int count = in.nextInt();
        int i = 1;
        do  {                                                             // 注意 while 处理多个 case
            int num = in.nextInt();
            String verifyStr = in.next();
            verifyStr += verifyStr;                                       // 模拟环形
            Map<Character, Integer> map = new HashMap<>();
            int minValue = verifyStr.length();
            for (int j = 0 ; j < verifyStr.length(); j++) {
                if (map.get(verifyStr.charAt(j)) != null) {
                    int distance = j - map.get(verifyStr.charAt(j)) - 1;  // 相同字符间间隔字符数
                    if (distance + 1 != num)                              // 相同字符距离等于起始字符长度,则无效
                        minValue = Math.min(j - map.get(verifyStr.charAt(j)) - 1, minValue);
                }
                map.put(verifyStr.charAt(j), j);
            }
            System.out.println(minValue == verifyStr.length() ? -1 : minValue);
        } while (i++ < count);
    }
}
发表于 2025-11-27 00:11:15 回复(0)
#include <iostream>
using namespace std;

int calcu(){
    string s;
    int n;
    cin >> n;
    cin >> s;
    if(n == 2) return -1;
    int mini = 1e5 + 5;
    s = "*" + s + s + "*";
    int len = s.size() / 2;
    for(int i = 1; i < len; i++){
        char c = s[i];
        if(s.find(c, i + 1) != -1){
            int l = s.find(c, i + 1) - i - 1;
            if(l == n - 1) continue;
            mini = min(mini, l);
        }
    }
    if(mini != 1e5 + 5) return mini;
    return -1;
}
int main() {
    int T;
    cin >> T;
    while(T--){
        cout << calcu() << endl;
    }
}
// 64 位输出请用 printf("%lld")
顷刻炼化!
发表于 2025-10-26 00:32:51 回复(0)
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <algorithm>
using namespace std;

int solve() {
    int n;
    string s;
    cin >> n;
    cin >> s;

    //检查相邻是否有相同的
    for (int i = 0; i < n; i++) {
        if (s[i] == s[(i + 1) % n]) {
            return 0;
        }
    }
    bool all_diff = true;
    vector<char> c(26);
    for (int i = 0; i < n; i++) {
        c[s[i] - 'a'] ++;
    }
    for (auto it : c) {
        if (it > 1) {
            all_diff = false;
            break;
        }
    }
    if (all_diff) {
        return -1;
    }
    //收集每种属性的位置
    map<char, vector<int>> pos;
    for (int i = 0; i < n; i++) {
        pos[s[i]].push_back(i);
    }
    //初始化为最大
    int ans = n - 1;
    for (char c = 'a'; c <= 'z'; c++) {
        if (pos[c].size() < 2) {
            continue;
        }
        int m = pos[c].size();
        for (int ch = 0; ch < m; ch++) {
            int dist = abs(pos[c][(ch + 1) % m] - pos[c][ch]);

            int gap1 = dist - 1;
            int gap2 = n - dist - 1;
            int min_gap = min(gap1, gap2);
            ans = min(ans, min_gap);
        }
    }
    return ans;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T;
    cin >> T;
    while (T--) {
        cout << solve() << "\n";
    }

    return 0;
}
发表于 2025-10-21 21:56:13 回复(0)
N = int(input())
while 1:
    try:
        n = int(input())
        ls = list(input())
        count = n
        code = {}
        for i in range(n):
            if ls[i] in code:
                code[ls[i]].append(i)
            else:
                code[ls[i]] = []
                code[ls[i]].append(i)
        psbls = []
        for x in code.items():
            if len(x[1])>1:
                psbls.append(x[0])
        if len(psbls) == 0:
            print(-1)
        else:
            for x in psbls:
                for i in range(len(code[x])-1):
                    if (code[x][i+1] - code[x][i]-1)<count:
                        count = code[x][i+1] - code[x][i]-1
                if (n - code[x][-1] + code[x][0] - 1)<count:
                    count = n - code[x][-1] + code[x][0] - 1
            print(count)

    except:
        break

发表于 2025-10-09 13:06:40 回复(0)
#include <climits>
#include <iostream>
#include <vector>
using namespace std;

int main() {

    int num;
    cin >> num;
    while (num--) {
        int stonenum;
        cin >> stonenum;
        string str;
        cin >> str;
        vector<vector<int>> store(26);
        for (int i = 0; i < str.size(); i++) {
            store[str[i] - 'a'].push_back(i);
        }

        // 不能破坏的情况
        int breakflag = 0;
        for (int i = 0; i < 26; i++) {
            if (store[i].size() <= 1) continue;
            else {
                breakflag = 1;
            }
        }
        if (breakflag == 0) {
            cout << -1 << endl;
            continue;
        }

        //  能破坏的情况
        int mindis = INT_MAX;
        for (int i = 0; i < 26; i++) {
            if (store[i].size() <= 1) continue;

            // 比较前后大小
            for (int j = 1; j < store[i].size(); j++) {
                int prediff = store[i][j] - store[i][j - 1] - 1;
                int backdiff = stonenum - store[i][j] + store[i][0] - 1;

                mindis = min(mindis, min(prediff, backdiff));
            }
        }

        cout << mindis << endl;
    }
}

发表于 2025-10-05 21:37:29 回复(0)
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    //思路:创建一个哈希表用来记录这个字符到下一个相同字符的最短距离就行了。可以
    //如果找到最后位置没有找到相同,则从字符串首尾相连的情况开始找。ok肯定能实现,看看大佬们又没更好的算法。

    int hash_count[26];  //-1表示这个字母没有出现或者没有另一个相同属性的宝石给他断
    //memset(hash_count, -1, sizeof(hash_count));
    int n;
    scanf("%d\n",&n);

    for(int i=1;i<=n;i++)
    {
        memset(hash_count, -1, sizeof(hash_count)); //每次使用都初始化
        int l;
        scanf("%d\n",&l);
        char* string=malloc(sizeof(char)*(l+1));
        scanf("%s",string);
       
        for(int j=0;string[j]!='\0';j++)
        {
            bool isfind=false;//如果找到末尾都没找到可能存在环形的路径
            for(int k=j+1;string[k]!='\0';k++)
            {
                if(string[j]==string[k]) //找到两个相同字符的距离
                {
                    isfind=true;  //找到了
                    if(hash_count[string[j]-'a']==-1) //没有出现过记录
                    {
                        hash_count[string[j]-'a']=k-j-1; //记录数据

                    }else {
                        if(k-j-1<hash_count[string[j]-'a'])  //当前的距离是否比旧的记录少
                        {
                            hash_count[string[j]-'a']=k-j-1; //记录更少数据
                        }
                    }
                    break;  //找到了就可以不用继续找了,应该是这个原因导致超时
                }
               
            }

            //没找到看看有没有环形路径
            if(isfind==false)
            {
                int mowei_l=l-1-j;//记录末尾的长度
                for(int s=0;s<j-1;s++)
                {
                    if(string[j]==string[s]) //找到从末尾开始的路径
                    {
                        if(hash_count[string[j]-'a']==-1) //没有出现过记录
                        {
                            hash_count[string[j]-'a']=mowei_l+s; //记录数据
                        }else {
                            if(mowei_l+s<hash_count[string[j]-'a'])  //当前的距离是否比旧的记录少
                            {
                                hash_count[string[j]-'a']=mowei_l+s; //记录更少数据
                            }
                        }
                        break;  //找到了就可以不用继续找了,应该是这个原因导致超时
                    }
                   
                }
            }
        }
        //输出结果找最小
        int min=l;  //默认最长就是l
        bool cunzai=false;//是否存在最小的路径
        for(int i=0; i<26;i++)
        {
            //printf("%c=%d\n",i+'a',hash_count[i]);
            if(hash_count[i]!=-1&&hash_count[i]<min)
            {
                cunzai=true;
                min=hash_count[i];
            }
        }

        if(cunzai==true)
        {
            printf("%d\n",min);
        }else {
            printf("%d\n",-1);
        }
    }
   
    return 0;
}
 
发表于 2025-10-02 16:45:53 回复(0)
#include <cstdint>
#include <iostream>
#include <sys/types.h>
#include <vector>
#include <map>
#include <climits>
#include <algorithm>

using namespace std;

int leastRemovedNum(const vector<char>& attributes) {
int ret{INT_MAX};
const auto n = attributes.size();
map<char, vector<int>> c_to_position_vec;

for(int i = 0; i < n; i++) {
c_to_position_vec[attributes[i]].push_back(i);
}

for(const auto& c_vec : c_to_position_vec) {
const auto& vec = c_vec.second;
const auto s = vec.size();
if(s < 2) continue;
for(int i = 1; i < s; i++) {
int a = vec[i] - 1 - vec[i-1];
if( i == s - 1 ) {
int b = vec[0] + n - 1 - vec[i];
a = min(a, b);
}
ret = min(ret, a);
}
}

return ret == INT_MAX? -1 : ret;
}

int main() {
// a组测试数据
uint64_t a{0};
cin >> a;

//读取每组测试数据
for(int i = 0; i < a ; i++) {
int n;
cin >> n;

vector<char> attributes;
attributes.reserve(n);
for(int j = 0; j < n; j++) {
char k;
cin >> k;
attributes.push_back(k);
}
cout << leastRemovedNum(attributes) << endl;
}

return 0;
}

发表于 2025-09-12 20:22:20 回复(0)
m=int(input())
for _ in range(m):
    n=int(input())
    m=input()
    if n==2 and m[0]!=m[1]:
        print(-1)
    else:
        last={}
        for j,k in enumerate(m):#abcdajlc
            if k not in last:
                last[k]=j
            else:
                print(j-last[k]-1)

            
            


发表于 2025-09-08 13:22:48 回复(0)
把原字符串再复制一份,模拟收尾相连的字符串,比如原字符串为ac,我们把它变为acac,
从头开始遍历字符串,用HashMap时刻记录各个元素最近出现的位置,求出距离最小值。
注意,如果遍历结束,距离最小值为字符串的长度 - 1,说明原字符串没有相同元素,返回-1。
否则返回距离的最小值。
import java.util.Scanner;
import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int T = in.nextInt();
        for (int i = 0; i < T; i++) {
            int n = in.nextInt();
            in.nextLine();
            String s = in.nextLine();
            String str = s + s;
            Map<Character, Integer> map = new HashMap<>();
            char[] chars = str.toCharArray();
            int min = Integer.MAX_VALUE;
            for (int j = 0; j < chars.length; j++) {
                if (map.containsKey(chars[j])) {
                    min = Math.min(min, j - map.get(chars[j]) - 1);
                }
                map.put(chars[j], j);
            }
            if (min == s.length() - 1) {
                System.out.println(-1);
            } else {
                System.out.println(min);
            }
        }
    }
}

发表于 2025-07-22 15:15:04 回复(0)
def compute(n,s):
    my_dict = {}
    # value是三元组 首位置 上一个位置 最小距离
    for i in range(n):
        a = s[i]
        if a in my_dict:        
            my_dict[a][2] = min(my_dict[a][2],min(i-my_dict[a][1]-1,my_dict[a][0]+n-1-i))
            my_dict[a][1] = i
        else:
            my_dict[a] = [i,i,n+1]

    ans = min([value[-1] for value in my_dict.values()])
    return ans if ans<n else -1


T = int(input())
for i in range(T):
    n = int(input())
    s = str(input())
    print(compute(n,s))

发表于 2025-07-09 20:52:19 回复(0)
"""
解题思路
检查相邻宝石:首先检查环形手串中是否存在相邻的相同宝石。如果存在,则无需摘除任何宝石,直接返回0。
记录每个宝石的位置:对于每个宝石属性,记录其在环形中的位置。
计算最小间隔:对于每个宝石属性,计算其所有出现位置之间的最小间隔(考虑环形结构)。这个间隔决定了需要摘除的宝石数量。
判断剩余宝石数量:根据最小间隔计算需要摘除的宝石数量,并确保剩余宝石数量至少为2,否则返回-1。
"""

import sys
from collections import defaultdict

def solve():
    input = sys.stdin.read().split()
    idx = 0
    T = int(input[idx])
    idx += 1
    for _ in range(T):
        n = int(input[idx])
        idx += 1
        s = input[idx]
        idx += 1
       
        # 检查是否存在相邻相同的字符(包括首尾)
        has_adjacent = False
        for i in range(n):
            if s[i] == s[(i+1)%n]:
                has_adjacent = True
                break
        if has_adjacent:
            print(0)
            continue
       
        # 记录每个字符的位置
        pos = defaultdict(list)
        for idx_c, c in enumerate(s):
            pos[c].append(idx_c)
       
        min_gap_all = float('inf')
        # 遍历每个字符的出现位置
        for c in pos:
            lst = pos[c]
            m = len(lst)
            if m < 2:
                continue
            lst.sort()
            min_gap = float('inf')
            for i in range(m):
                current = lst[i]
                next_p = lst[(i+1) % m]
                # 计算顺时针方向的间隔
                if next_p >= current:
                    clockwise = next_p - current
                else:
                    clockwise = next_p + n - current
                # 逆时针方向的间隔是n - clockwise
                gap = min(clockwise, n - clockwise)
                if gap < min_gap:
                    min_gap = gap
            if min_gap < min_gap_all:
                min_gap_all = min_gap
       
        if min_gap_all == float('inf'):
            # 所有字符只出现一次,无法形成间隔
            print(-1)
        else:
            k = min_gap_all - 1
            if n - k >= 2:
                print(k)
            else:
                print(-1)

if __name__ == '__main__':
    solve()

"""
代码解释
输入处理:读取输入数据并处理多组测试用例。
相邻检查:遍历每个宝石及其下一个宝石(包括首尾相接的情况),检查是否存在相邻相同的宝石。
位置记录:使用字典记录每个宝石属性的所有出现位置。
最小间隔计算:对于每个宝石属性,计算其所有出现位置之间的最小间隔(考虑环形结构)。
结果判断:根据最小间隔计算需要摘除的宝石数量,并确保剩余宝石数量至少为2,否则返回-1。
"""


发表于 2025-05-01 22:40:44 回复(0)