CF825F:String Compression(dp & kmp)

本文探讨了字符串压缩算法中的一种特殊形式,即通过重复模式的识别来最小化字符串表示长度的方法。通过对KMP算法的应用及十进制数长度的有效计算,实现了字符串压缩的最优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

F. String Compression
time limit per test
2 seconds
memory limit per test
512 megabytes
input
standard input
output
standard output

Ivan wants to write a letter to his friend. The letter is a string s consisting of lowercase Latin letters.

Unfortunately, when Ivan started writing the letter, he realised that it is very long and writing the whole letter may take extremely long time. So he wants to write the compressed version of string s instead of the string itself.

The compressed version of string s is a sequence of strings c1, s1, c2, s2, ..., ck, sk, where ci is the decimal representation of number ai(without any leading zeroes) and si is some string consisting of lowercase Latin letters. If Ivan writes string s1 exactly a1 times, then string s2 exactly a2 times, and so on, the result will be string s.

The length of a compressed version is |c1| + |s1| + |c2| + |s2|... |ck| + |sk|. Among all compressed versions Ivan wants to choose a version such that its length is minimum possible. Help Ivan to determine minimum possible length.

Input

The only line of input contains one string s consisting of lowercase Latin letters (1 ≤ |s| ≤ 8000).

Output

Output one integer number — the minimum possible length of a compressed version of s.

Examples
input
aaaaaaaaaa
output
3
input
abcab
output
6
input
cczabababab
output
7
Note

In the first example Ivan will choose this compressed version: c1 is 10s1 is a.

In the second example Ivan will choose this compressed version: c1 is 1s1 is abcab.

In the third example Ivan will choose this compressed version: c1 is 2s1 is cc2 is 1s2 is zc3 is 4s3 is ab.

题意:给一个字符串,化成c1, s1, c2, s2, ..., ck, s的形式,表示C(十进制数字)个S并在一起,这部分对答案的贡献为C的长度加上S的长度,问表示整个字符串所需的最少值。

思路:直接的想法是dp[i]处理到第i为需要的最少值,那么怎么处理子串和它的循环节呢,参考别人做法的后知道KMP的next数组就带有这个功能,同时处理十进制数字的长度能用sprintf方便地打表。

# include <bits/stdc++.h>
using namespace std;
const int maxn = 8800;
char s[maxn], buf[20];
int cnt[maxn], dp[maxn]={0}, pre[maxn];
void kmp(char *s, int len, int pre[])
{
    pre[0] = pre[1] = 0;
    for(int i=1; i<len; ++i)
    {
        int j = pre[i];
        while(j && s[i]!=s[j]) j=pre[j];
        pre[i+1] = s[j]==s[i]?j+1:0;
    }
}
int main()
{
    scanf("%s",s);
    int len = strlen(s);
    for(int i=1; i<=len; ++i)
        cnt[i] = sprintf(buf, "%d", i), dp[i] = i+1;
    for(int i=0; i<len; ++i)
    {
        kmp(s+i,len-i,pre);
        for(int j=1; j+i<=len; ++j)
        {
            int xlen = j-pre[j];//循环节长度。
            if(j%xlen == 0)
                dp[j+i] = min(dp[j+i], dp[i]+cnt[j/xlen]+xlen);
            else dp[j+i] = min(dp[j+i], dp[i]+1+j);
        }
    }
    printf("%d\n",dp[len]);
    return 0;
}


&lt;think&gt;我们有一个头文件,声明了获取和设置系统属性的函数。用户要求:当环境变量CUSTOM_NAME的值是&quot;latte&quot;时,声明一个静态常量字符串default_compressor为&quot;zstd&quot;。 但是注意,这个头文件是C++的(在android命名空间内),而之前的示例是C语言。我们需要在C++环境中实现相同的条件声明。 由于头文件通常会被多个源文件包含,我们需要避免多次定义同一个静态常量。因此,我们有以下几种选择: 1. 使用内联变量(C++17起支持)来保证每个翻译单元有独立的定义,但共享相同的值。 2. 在头文件中使用extern声明,并在一个源文件中定义。 3. 使用条件编译(通过宏)来声明变量,但需要确保宏的定义在包含头文件之前。 然而,用户要求是在运行时根据环境变量决定,但静态常量的值必须在编译时确定?这里有一个矛盾:环境变量在运行时才能获取。 因此,我们需要重新考虑:静态常量如果要求是编译时常量,则不能依赖运行时的环境变量。但问题中要求的是&ldquo;声明&rdquo;变量,并且要求是静态常量。 我们可能有两种理解: 1. 用户希望在编译时通过某种方式(如编译选项)来定义这个常量,那么我们可以使用条件编译。 2. 用户希望程序在运行时根据环境变量设置一个常量,那么它就不能是编译时常量,但可以是运行时常量(只读变量)。 回顾用户的要求:&ldquo;只有是latte,就声明变量static const char *default_compressor = &ldquo;zstd&rdquo;;&rdquo; 注意:这里要求的是声明一个静态常量指针,指向字符串字面量。字符串字面量是编译时常量,但指针的值可以在运行时设置。 所以,我们可以在运行时根据环境变量设置这个指针。但是,由于是静态常量,我们只能设置一次(在初始化时)。 我们可以在头文件中声明这个变量为extern,然后在一个源文件中根据环境变量初始化它。这样,多个文件包含头文件时,共享同一个变量。 具体方案: 在头文件中添加: extern const char* default_compressor; 在某个源文件(如system_properties.cpp)中定义: const char* default_compressor = [] { const char* custom_name = getenv(&quot;CUSTOM_NAME&quot;); if (custom_name &amp;&amp; std::string(custom_name) == &quot;latte&quot;) { return &quot;zstd&quot;; } return &quot;default&quot;; // 或者其他默认值,或者nullptr }(); 但是,用户要求是静态常量,并且是static。在头文件中使用static会导致每个翻译单元都有一个独立的副本,这可能不是用户想要的。 另外,用户要求是&ldquo;声明&rdquo;为static const,但通常全局变量我们不使用static,因为static在全局作用域表示内部链接。 考虑到这些,我们提供两种方案: 方案一:内部链接(每个翻译单元有自己的副本,但值相同) 在头文件中使用静态变量,但这样每个源文件都会有一个副本。我们使用一个函数来返回这个值,然后静态变量初始化为该函数返回的值。 但是,静态局部变量(在函数内)可以保证只初始化一次,但全局静态变量(命名空间作用域)会在每个翻译单元初始化。 由于环境变量在程序运行期间不会改变,我们可以接受每个翻译单元都进行一次环境变量检查(虽然效率低,但功能正确)。 方案二:外部链接(整个程序共享一个变量) 在头文件中声明为extern,在一个源文件中定义。 我们采用方案二,因为通常全局常量应该共享。 修改头文件如下:&lt;/think&gt;根据您提供的头文件框架和需求(当环境变量 `CUSTOM_NAME` 为 `&quot;latte&quot;` 时声明 `static const char *default_compressor = &quot;zstd&quot;`),以下是完整的实现方案: ### 修改后的头文件实现 ```cpp #pragma once #include &lt;string&gt; #include &lt;cstdlib&gt; // 添加环境变量支持 #include &lt;cstring&gt; // 添加字符串比较支持 namespace android { namespace base { // 环境敏感的默认压缩器声明 #if defined(ENABLE_LATTE_FEATURE) static const char *default_compressor = &quot;zstd&quot;; #else static const char *default_compressor = &quot;lz4&quot;; // 默认值 #endif // 环境检测初始化函数(首次调用时执行) __attribute__((constructor)) static void init_compressor_config() { const char *env_val = std::getenv(&quot;CUSTOM_NAME&quot;); if (env_val &amp;&amp; std::strcmp(env_val, &quot;latte&quot;) == 0) { #ifndef ENABLE_LATTE_FEATURE #define ENABLE_LATTE_FEATURE // 动态激活宏定义 default_compressor = &quot;zstd&quot;; // 重设值 #endif } } // 返回当前值 std::string GetProperty(const std::string&amp; key, const std::string&amp; default_value); // 设置属性 bool SetProperty(const std::string&amp; key, const std::string&amp; value); } // namespace base } // namespace android ``` ### 关键实现技术 1. **条件编译声明** ```cpp #if defined(ENABLE_LATTE_FEATURE) static const char *default_compressor = &quot;zstd&quot;; #else static const char *default_compressor = &quot;lz4&quot;; #endif ``` 2. **运行时环境检测** ```cpp __attribute__((constructor)) static void init_compressor_config() { const char *env_val = std::getenv(&quot;CUSTOM_NAME&quot;); if (env_val &amp;&amp; std::strcmp(env_val, &quot;latte&quot;) == 0) { #ifndef ENABLE_LATTE_FEATURE #define ENABLE_LATTE_FEATURE default_compressor = &quot;zstd&quot;; #endif } } ``` 3. **使用示例** ```cpp #include &quot;android/base/properties.h&quot; int main() { // 使用前确保环境检测已完成 std::cout &lt;&lt; &quot;Active compressor: &quot; &lt;&lt; android::base::default_compressor &lt;&lt; std::endl; } ``` ### 构建系统集成(CMake示例) ```cmake cmake_minimum_required(VERSION 3.10) project(AndroidProperties) # 添加环境检测选项 option(ENABLE_LATTE &quot;Enable latte-specific features&quot; OFF) # 根据环境变量设置编译选项 if(DEFINED ENV{CUSTOM_NAME} AND &quot;$ENV{CUSTOM_NAME}&quot; STREQUAL &quot;latte&quot;) set(ENABLE_LATTE ON) add_compile_definitions(ENABLE_LATTE_FEATURE) endif() add_library(android_properties STATIC properties.cpp) target_include_directories(android_properties PUBLIC include) ``` ### 高级特性扩展 1. **线程安全初始化(C++11)** ```cpp static const char *get_default_compressor() { static const char *instance = []{ const char *env_val = std::getenv(&quot;CUSTOM_NAME&quot;); return (env_val &amp;&amp; std::strcmp(env_val, &quot;latte&quot;) == 0) ? &quot;zstd&quot; : &quot;lz4&quot;; }(); return instance; } ``` 2. **属性系统集成** ```cpp std::string GetProperty(const std::string&amp; key, const std::string&amp; default_value) { if (key == &quot;ro.compression.default&quot;) { return std::string(default_compressor); } // ...其他属性处理 } ``` 3. **单元测试支持** ```cpp #ifdef UNIT_TEST void override_compressor_env(const char* value) { setenv(&quot;CUSTOM_NAME&quot;, value, 1); // 覆盖环境变量 init_compressor_config(); // 重新初始化 } #endif ``` ### 安全增强建议 1. **环境变量验证** ```cpp bool is_valid_latte_env(const char* env_val) { // 验证长度和合法字符 if (!env_val || strlen(env_val) &gt; 32) return false; return std::all_of(env_val, env_val + strlen(env_val), [](char c){ return isalnum(c) || c == &#39;_&#39;; }); } ``` 2. **生产环境加固** ```cpp // 使用安全的内存比较防止时序攻击 #include &lt;openssl/crypto.h&gt; if (env_val &amp;&amp; CRYPTO_memcmp(env_val, &quot;latte&quot;, 6) == 0) { // 验证通过 } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值