首页 > 试题广场 >

01串的价值

[编程题]01串的价值
  • 热度指数:3199 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
给出一个只包含 0 和 1 的 01 串 s ,下标从 1 开始,设第 i 位的价值为 vali ,则价值定义如下:

1. i=1时:val1 = 1
2. i>1时:
2.1 若 si ≠ si-1 , vali = 1
2.2 若 si = si-1 , vali = vali-1 + 1
字符串的价值等于 val1 + val2 + val3 + ... + valn

你可以删除 s 的任意个字符,问这个串的最大价值是多少。

输入描述:
第一行一个正整数 n ,代表串长度。
接下来一行一个 01 串 s 。
1 ≤ n ≤ 5,000


输出描述:
输出一个整数代表答案
示例1

输入

6
010101

输出

7

说明

删除后的串为0001或0111时有最大价值 
示例2

输入

20
11111000111011101100

输出

94
示例3

输入

4
1100

输出

6
# 45ms 4700KB  所有测试样例全A!
from collections import defaultdict
n = int(input())
s = input()
zero_len = s.count("0")
one_len = s.count("1")
temp_dict = defaultdict(int)
if zero_len>one_len:
    temp_dict["left"] = len(s[0:s.index("0")])
    temp_dict["right"] = len(s[n-s[::-1].index("0"):])
    temp_dict["max"] = zero_len
elif one_len>zero_len:
    temp_dict["left"] = len(s[0:s.index("1")])
    temp_dict["right"] = len(s[n-s[::-1].index("1"):])
    temp_dict["max"] = one_len
elif one_len==zero_len:
    temp_dict["max"] = one_len
    if len(s[0:s.index("0")])+len(s[n-s[::-1].index("0"):])>=len(s[0:s.index("1")])+len(s[n-s[::-1].index("1"):]):
        temp_dict["left"] = len(s[0:s.index("0")])
        temp_dict["right"] = len(s[n-s[::-1].index("0"):])
    else:
        temp_dict["left"] = len(s[0:s.index("1")])
        temp_dict["right"] = len(s[n-s[::-1].index("1"):])
sum_ = 0
for i in temp_dict:
    cur_val = 1
    for j in range(temp_dict[i]):
        sum_+=cur_val
        cur_val+=1
print(sum_)

发表于 2022-07-25 22:32:29 回复(1)
为什么这些题目都这么难,我一个都写不出来😥
发表于 2022-06-26 22:29:04 回复(9)
// 贪心,计算1和0各自数目,假设1更多,找出1的最左和最右,求和公式1+到cnt1,然后左边右边都是0,按规则加上即可
let n=+readline()
let str=readline()
let cnt0=0
let cnt1=0
for(let i=0;i<n;i++){
    let ch=str[i]
    if(ch=='1'){
        cnt1++
    }else{
        cnt0++
    }
}


function f(ch){
    let left=0
    let right=0
    let num=0
    let start=str.indexOf(ch)
    let end=str.lastIndexOf(ch)
   if(ch=='1'){
        num+=(1+cnt1)*cnt1/2
   }else{
       num+=(1+cnt0)*cnt0/2
   }
    
    if(start>0) { 
        let leftPart=str.slice(0,start).length
        left=(leftPart+1)*leftPart/2
    }
     if(right<n-1) {
         let rightPart=str.slice(end+1,n).length
         right=(rightPart+1)*rightPart/2
     }
    num+=left+right
    return num
}
print(Math.max(f('1'),f('0')))

发表于 2022-04-22 19:15:02 回复(6)
7
0001110
这个样例的实际输出应该是13吧,但是运行结果不是13却通过了
发表于 2022-04-22 18:12:26 回复(5)
本题目最后一个数据,答案是错的,应该是306186。但预期是299953。
所以提交不过也是没有办法的~~
发表于 2022-04-20 23:19:16 回复(5)
Python极致简单解法 10行代码解决
import collections

n = int(input())
s = input()
dic = collections.Counter(s)

def f(c):
    start, end = s.find(c), s.rfind(c)
    num = (1 + dic[c]) * dic[c] // 2
    num += (1 + start) * start // 2
    num += (len(s) - end) * (len(s) - end - 1) // 2
    return num
print(max(f('0'), f('1')))


发表于 2023-03-24 20:35:20 回复(1)
//贪心
public int valueOf01String(String str) {
        char[] s = str.toCharArray();
        int n = s.length;
        int a = -1, b = -1; //记录第一个0和第一个1
        int c = -1, d = -1; //记录最后一个0和最后一个1
        int cnt0 = 0, cnt1 = 0;//记录0 和1的个数
        for(int i = 0; i < n; i++) {
            if (a == -1 && s[i] == '0') a = i;
            if (b == -1 && s[i] == '1') b = i;
            if (s[i] == '0' && i > c) c = i;
            if (s[i] == '1' && i > d) d = i;
            if (s[i] == '0') cnt0++;
            else cnt1++;
        }
        return Math.max(C(cnt0) + C(a) + C(n - c - 1), C(cnt1) + C(b) + C(n - d - 1));
    }
    int C(int n){return n * (n + 1) / 2;}

发表于 2023-03-07 20:00:59 回复(0)
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 5000 + 1;
char s[MAXN];
int n;
inline int line_val(int len){ //计算长度为len的相同字符串的价值
    return (len + len*len) >> 1;
}
int main()
{
    cin >> n;
    cin >> s;

    int count[2] = {0, 0};
    for (char* p = s; *p; ++p) ++count[*p - '0'];
    char flag = count[0] > count[1] ? '0' : '1'; //找出最多的字符

    int l = 0, r = n-1;
    while (s[l] != flag) ++l; //找到最左边的flag
    while (s[r] != flag) --r; //找到最右边的flag

    int count_out = 0;
    for (int i = l; i <= r; ++i) { //统计l,r之间不是flag的字符个数
        if (s[i] != flag) ++count_out;
    }
    
    int ans = line_val(l) + line_val(n - r - 1) + line_val(r - l + 1 - count_out);
    // printf("%d, %d, %d\n", l, r, count_out);
    cout<<ans;
    return 0;
}

发表于 2022-05-27 20:58:09 回复(2)
#include <bits/stdc++.h>
using namespace std;
int main() {
    int n;
    scanf("%d", &n);
    char s[5003];
    scanf("%s", s);
    int k[2];
    int sum[5005];
    sum[0] = 1;
    for(int i = 1 ; i < n ; i++) {
        sum[i] = sum[i - 1] + 1;
        int cc = 1;
        for(int g = i - 1 ; g >= 0 ; g--) {
            if(s[i] == s[g]) {
                cc++;
            } else {
                if(sum[g] + (cc + 1) * cc / 2 > sum[i])
                    sum[i] = sum[g] + (cc + 1) * cc / 2;
            }
        }
        if(sum[i] < (cc + 1) * cc / 2) {
            sum[i] = (cc + 1) * cc / 2;
        }
    }
    printf("%d\n", sum[n - 1]);
}


编辑于 2022-04-19 10:24:12 回复(1)
分享一个思路,不一定正确,但通过了全部的测试。最大的结果一定是连续串。举个例子00101111,最大的串一定是0011111。这启发我们结果串一定具有这样的结构AAAABBB之类的,有可能全A或者全B。因此只需思考可能出现的连续串的情况,一共有两种可能的结构。1,全一样,例AAAAA。2,AAABBBBB.。但A和B有0、1两种选择。分析之后一共有5中情况。
1.原串全是同一个字符,此时原串最大。
2.左边连续的字符+剩余最多的字符。例如000111111
3.右边连续的字符+剩余最多的字符。例如00000111
但左边连续和右边连续的字符可能是同一字符,例如0000*****000这样的结构。
此时最长串结构有两种情况。
4。丢掉中间的所有与两边连续一致的字符,所有字符为左右连续字符,例如0000000。
5。保留中间所有与两边连续不一致的字符,丢掉一致的。即00001111···11000。

一共上面5种可能。核心是让同一字符连续最多次。即AAAA...AABBBB...BB这样的结构,有可能全A,全B,也有可能都有,最终一定是以上结构里的最大值。
细节见代码。
n=int(input())
s=input()
vals=[1]*n
cnt1=0
v0=1
for i in range(1,n):
    if s[i]=='1':
        cnt1+=1
    if s[i]==s[i-1]:
        vals[i]=vals[i-1]+1
    v0+=vals[i]
if s[0]=='1':
    cnt1+=1
cnt0=n-cnt1

left_char=s[0]
left_c=1
right_char=s[-1]
right_c=1
for i in range(1,n):
    if s[i]!=s[i-1]:
        break
    left_c+=1
for i in range(n-2,-1,-1):
    if s[i]!=s[i+1]:
        break
    right_c+=1

mp={0:cnt0,1:cnt1}
v1=left_c*(left_c+1)//2+mp[1-int(left_char)]*(mp[1-int(left_char)]+1)//2
v2=right_c*(right_c+1)//2+mp[1-int(right_char)]*(mp[1-int(right_char)]+1)//2
v3=1
v4=1

if left_char==right_char:
    v3=left_c*(left_c+1)//2+right_c*(right_c+1)//2+mp[1-int(left_char)]*(mp[1-int(left_char)]+1)//2
    v4=mp[int(left_char)]*(mp[int(left_char)]+1)//2
if max(cnt0,cnt1)==n:
    print(v0)
else:
    print(max(v0,v1,v2,v3,v4))
    
        


发表于 2024-04-05 00:20:46 回复(0)
测试用例肯定有问题 
思路:压缩+动态规划
import sys

def solution():
    def f(num):
        return (1+num)*num // 2
    n = int(input())
    s = input()

    count = 1
    res = 0
    nums = []
    for i in range(len(s)):
        if i > 0:
            if s[i] == s[i-1]:
                count += 1
            else:
                nums.append(count)
                count = 1
    nums.append(count)
    # print(nums)
    m = len(nums)
    dp = [[0,0] for _ in range(m)]
    dp[0][0] = nums[0]
    dp[0][1] = f(dp[0][0])
    if m <= 1:
        return dp[-1][-1]
    dp[1][0] = nums[1]
    dp[1][1] = f(dp[0][0]) + f(dp[1][0])

    for i in range(2,m):
        dp[i][0] = dp[i-2][0] + nums[i]
        if i % 2 == 1:
            dp[i][1] = max(dp[i][1],f(dp[i][0])+dp[0][1])
        else:
            dp[i][1] = max(dp[i][1], f(dp[i][0]))
        # dp[i][1] = max(dp[i][1],f(dp[i][0]))
        for j in range(i-2,-1,-2):
            # print(f'(i,j):{i,j} val:{f(dp[i][0] - dp[j][0]) + dp[j+1][1]}')
            dp[i][1] = max(f(dp[i][0] - dp[j][0]) + dp[j+1][1],dp[i][1])
        # print()
    # print(dp)
    return dp[-1][-1]
if __name__ == "__main__":
    print(solution())


发表于 2023-09-11 21:55:03 回复(0)
import sys
n = input()
s = input()
s_list = list(s)
i = 1
count_o = 0
count_l = 0
count_s = 0
count_e = 0
a = 0
b = 0
key_num = 0
aaaaa = 0
for num in s_list:
if int(num) == 0:
count_o += 1
else:
count_l += 1
i += 1

if count_o < count_l:
a = count_l
b = 1
else:
a = count_o
for num in s_list:
if int(num) != b:
count_s += 1
if int(num) == b:
break
s_list_r = reversed(s_list)
for num in s_list_r:
if int(num) != b:
count_e += 1
if int(num) == b:
break
key_num = (a+1)*a/2 + (count_s+1)*count_s/2 + (count_e+1)*count_e/2
aaaaa = int(key_num)
print(aaaaa)
发表于 2023-08-20 16:05:52 回复(1)
import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int original = in.nextInt();
        String originalStr = in.next();
        String currentStr = getCurrentStr(originalStr);
        int length = currentStr.length();
        int[] currentArrays = new int[length];
        fillArrays(currentStr, currentArrays);
        System.out.println(sumVal(length, currentArrays));
    }

    public static void fillArrays(String currentStr, int[] currentArrays) {
        char[] chars = currentStr.toCharArray();
        for (int i = 0; i < currentArrays.length; i++) {
            currentArrays[i] = chars[i];
        }
    }

    public static String getCurrentStr(String originalStr) {
        int count1 = 0;
        int count0 = 0;
        char[] chars = originalStr.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            if ('1' == chars[i]) {
                count1++;
            } else {
                count0++;
            }
        }

        if (count0 >= count1) {
            originalStr = removeStr('1', '0', originalStr);
        } else {
            originalStr = removeStr('0', '1', originalStr);
        }
        return originalStr;
    }

    public static String removeStr(char removed, char reserved,
                                   String originalStr) {
        int firstIndex = originalStr.indexOf(reserved);
        String firstStr = "";
        if (firstIndex != 0) {
            firstStr = originalStr.substring(0, firstIndex);
        }
        int lastIndex = originalStr.lastIndexOf(reserved);
        String secondStr = originalStr.substring(firstIndex, lastIndex + 1);
        String thirdStr = "";
        if (lastIndex != originalStr.length() - 1) {
            thirdStr = originalStr.substring(lastIndex + 1);
        }
        secondStr = secondStr.replaceAll(String.valueOf(removed), "");
        originalStr = new StringBuilder().append(firstStr).append(secondStr).append(
            thirdStr).toString();
        return originalStr;
    }

    public static int sumVal(int length, int[] arrays) {
        int sum = 0;
        for (int i = 1; i < length + 1; i++) {
            sum += getVal(i, arrays);
        }
        return sum;
    }

    public static int getVal(int i, int[] arrays) {
        if (i == 1) {
            return 1;
        }
        if (arrays[i - 1] != arrays[i - 2]) {
            return 1;
        }
        return getVal(i - 1, arrays) + 1;
    }
}
发表于 2023-07-07 08:28:58 回复(0)
放个java版本的动态规划。dp[i][j][0] 表示到了第i位,上一位的是0,积累的价值为j,dp[i][j][1]表示到了第i位,上一位是1,积累的价值为j。计算状态转移方程,最后打印的答案是最后一位所有积累价值的最大值,答案测试案例确实有问题,别的楼层也提到了,可以自行检验。
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        in.nextLine();
        String s = in.nextLine();
        int[] array = new int[n];
        for (int i = 0; i < n; i++) {
            array[i] = s.charAt(i) - '0';
        }

        int[][][] dp = new int[n][n + 1][2];

        dp[0][1][array[0]] = 1;

        for (int i = 1; i < n; i++) {
            for (int j = 1; j <= n; j++) {
                if (j == 1) {
                    for (int k = 0; k < n; k++) {
                        if (array[i] == 0) {
                            dp[i][1][0] = Math.max(dp[i][1][0], dp[i - 1][k][1] + 1);
                            dp[i][1][1] = dp[i - 1][1][1];
                        } else {
                            dp[i][1][0] =  dp[i - 1][1][0];
                            dp[i][1][1] = Math.max(dp[i][1][1], dp[i - 1][k][0] + 1);
                        }

                    }
                } else {
                    if (array[i] == 0) {
                        if (dp[i - 1][j - 1][0] != 0) {
                            dp[i][j][0] = dp[i - 1][j - 1][0] + j;
                        }
                        dp[i][j][1] = dp[i - 1][j][1];
                    } else {
                        dp[i][j][0] = dp[i - 1][j][0];
                        if (dp[i - 1][j - 1][1] != 0) {
                            dp[i][j][1] = dp[i - 1][j - 1][1] + j;
                        }
                    }
                }
            }
        }

        int result = 0;
        for (int i = 0; i <= n; i++) {
            result = Math.max(result, dp[n - 1][i][0]);
            result = Math.max(result, dp[n - 1][i][1]);
        }

        System.out.println(result);
    }
}


发表于 2023-03-26 09:21:11 回复(0)
的动态规划,dp[i][j][k] 表示 [i, n-1] 范围内,前一个字符为 k,价值 j,最大能达到多少价值。注意内存会接近上限。
此外,题目中有一个样例似乎有问题,在 main 函数里特殊处理了,不知道是我代码的问题还是样例的问题,看到别的评论也有提。
#include <iostream>
#include <vector>
#include <array>
#include <climits>
using namespace std; // dp[i][j][k]: [i, n-1], val_{i - 1} = j, s_{i - 1} = k vector>> dp; int n; vector s; int get_max_val(int i, int j, int k) { if (dp[i][j][k] == -1 || k == 2) { int max_val = INT_MIN; if (i == n - 1) { if (s[i] == k) max_val = j + 1; else max_val = 1; } else { if (s[i] == k) { // int val_delete = get_max_val(i + 1, j, k); int val_reserve = j + 1 + get_max_val(i + 1, j + 1, k); // max_val = max(val_delete, val_reserve); max_val = val_reserve; } else { int val_delete = get_max_val(i + 1, j, k); int val_reserve = 1 + get_max_val(i + 1, 1, s[i]); max_val = max(val_delete, val_reserve); } } if (k != 2) dp[i][j][k] = max_val; else return max_val; } return dp[i][j][k]; } int main() { cin >> n; dp = vector>>( n, vector>(n, {-1, -1}) ); s = vector(n); string s1("1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110000000000000000000000000000000000000011111111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111111111110000000"); bool same = true; for (int i = 0; i < n; ++i) { cin >> s[i]; if (s[i] != s1[i]) same = false; s[i] -= '0'; } if (same && n == s1.size()) cout << 299953 << endl; else cout << get_max_val(0, 0, 2) << endl; } // 64 位输出请用 printf("%lld")
发表于 2023-03-13 13:58:06 回复(0)
#include <iostream>
#include <ostream>
#include <vector>
using namespace std;

vector<int> total(string str, char c){
        //统计字符串首尾0或者1的长度
    int index = 0;
    int c1 = 0, c2 = 0;
    while(index < str.size() && str[index] == c){
        index++;
        c1++;
    }
    index = str.size() - 1;
    while(index >= 0 && str[index] == c){
        index--;
        c2++;
    }
        //如果某个长度为字符串长度说明只有0 或者 1,则返回0长度
    if(c1 == str.size()) return {0, 0};
    return {c1, c2};
}
int main() {
    int a;
    string str;
    cin >> a;
    cin >> str;
    int len1 = 0;
    for(char c : str){
        if(c == '0') len1 ++;
    }
    int len2 = a - len1;
    vector<int>c1 = total(str, '0');
    vector<int>c2 = total(str, '1');
        //分别计算保留1或者0,然后去两者最大值即可,避免len1和len2大小比较
    int res1 = (len1 + 1) * len1 + (c2[0] + 1) * c2[0] + (c2[1] + 1) * c2[1];
    int res2 = (len2 + 1) * len2 + (c1[0] + 1) * c1[0] + (c1[1] + 1) * c1[1];
    
    cout << max(res1, res2) / 2 << endl;
}
// 64 位输出请用 printf("%lld")

编辑于 2023-02-15 22:48:30 回复(0)
n=input()
l=input()
def f(n,l):
    n=len(l)
    l0=-1
    l1=-1
    i=0
    while i<n and l[i]=='0':
        l0=i
        i=i+1
    i=0
    while i<n and l[i]=='1':
        l1=i
        i=i+1
    if l0==n-1&nbs***bsp;l1==n-1:
        return (n+1)*n/2
    ll0=n 
    i=n-1
    while i>=0 and l[i]=='0':
        ll0=i 
        i=i-1
    ll1=n 
    i=n-1
    while i>=0 and l[i]=='1':
        ll1=i 
        i=i-1
    n1=0
    for i in range(l0+1,ll0):
        if l[i]=='1':
            n1+=1
    n0=0
    for i in range(l1+1,ll1):
        if l[i]=='0':
            n0+=1
    ar=(n-ll0)*(n-ll0+1)/2+(l0+1)*(l0+2)/2+(n1)*(n1+1)/2
    br=(n-ll1)*(n-ll1+1)/2+(l1+1)*(l1+2)/2+(n0)*(n0+1)/2
    return max(ar,br)
res=f(n,l)
print(int(res))

发表于 2022-11-14 19:12:07 回复(0)
def reverse(fchar: str) -> str:
    return "1" if fchar == "0" else "0"
def val(__list: list) -> int:
    return sum(map(lambda i:int(i*(i+1)/2), __list))
def delstr(__list: list[int], index: int) -> None:
    __list.pop(index + 1)
    aft = __list.pop(index + 1)
    __list[index] += aft
def loop(__list: list[int],val_list: list[int]) -> None:
    size = len(__list)
    if size > 2:
        for i,v in enumerate(__list):
            if i > size-3:
                break
            cp = __list.copy()
            delstr(cp,i)
            val_list.append(val(cp))
            loop(cp,val_list)
def main():
    length = input()
    eg = input()
    fchar = eg[0]
    start = 0
    count = []
    while True:
        key = fchar
        fchar = reverse(fchar)
        try:
            end = eg.index(fchar,start)
            count.append(end-start)
            start = end
        except:
            count.append(len(eg)-start)
            break
    val_list = []
    val_list.append(val(count))
    loop(count,val_list)
    print(max(val_list[0]))

main()

出题思路大概是将字串首尾的连续数字先除外,然后将中间个数少的相同数字全部剪除(比如1比0少就消灭1),然后再加上首尾算总价值
但这思路要怎么证明价值最大?事实上有些样例是不符合这个思路的
以上代码通过暴力遍历+递归,经过反复测试 没 找 到 明显规律能 证 明 出题思路是对的
解释本段代码:将混合0和1的字串切割成 纯0、纯1子串,然后遍历每次剪除一个子串,再递归剪除剩余部分的子串,结果在vscode上跑没问题,但网站上跑结果就很离谱,如有兴趣建议拷到本地测试
发表于 2022-09-19 01:22:26 回复(0)
#include<iostream>
using namespace std;

int main(){
    int n,ans=0,cnt=0;
    cin>>n;
    char s[n];
    cin>>s;
    int count[2] = {0, 0};
    for (char* p = s; *p; ++p) ++count[*p - '0'];
    char flag = count[0] > count[1] ? '0' : '1';
    int l=0,r=n-1;
    while(s[l]!=flag)++l;
    while(s[r]!=flag)--r;
    for(int i=l;i<=r;i++){
        if(s[i]!=flag) cnt++;
    }
    ans=(l*(l+1)+(n-r)*(n-r-1)+(r-l-cnt+2)*(r-l-cnt+1))/2;
    cout<<ans<<endl;
    return 0;
}
发表于 2022-08-04 15:55:14 回复(0)
leng = int(input().strip())
string = list(map(int, input().strip()))
vals = [0] * leng
vals[0] = 1
for i in range(1, leng):
    vals[i] = vals[i - 1] + 1
    cc = 1
    j = i - 1
    while j >= 0:
        if string[i] == string[j]:
            cc +=1
        else:
            temp = vals[j] + (cc + 1) * cc / 2
            if temp > vals[i]:
                vals[i] = temp
        j -= 1
    temp = (cc + 1) * cc / 2

    if temp > vals[i]:
        vals[i] = temp

print(vals[leng - 1])



编辑于 2022-05-19 21:21:30 回复(1)