字符串中连续子串出现次数统计,获取连续出现次数最多的子串

本文介绍了一种在C++中实现的子串统计方法,包括如何找出字符串中的所有子串、统计每个子串的出现频率及连续出现的次数。通过具体实例展示了不同子串统计的应用场景。
// STR.cpp : 定义控制台应用程序的入口点。
//

#include "stdio.h"
#include "stdlib.h"
#include "iostream"
#include "string"
#include "vector"
#include "map"

using namespace std;
vector<string> FindAllSubString(string str);
map<string, int> SubStrCount(string str);
map<string, int> FindAllSuccessSubString(string str);

int main(int argc, char * argv[])
{
	//string name = "wei song";

	//string subName = name.substr(0,5);
	//string sub2 = name.substr(0, 0);
	//cout<<sub2<<endl;
	//cout<<subName<<endl;
	//cout<<name<<endl;

	// test
	string test = "abcbcbcabc";
	vector<string> result = FindAllSubString(test);

	for(int i = 0; i < result.size(); i++)
	{
		cout<<result[i]<<endl;
	}

	map<string, int> m_result = SubStrCount(test);
	cout<<"////////////////////////////////////////////////////////"<<endl;
	for(map<string, int>::iterator iter = m_result.begin(); iter != m_result.end(); iter++)
	{
		cout<<iter->first<<"    "<<iter->second<<endl;
	}

	// 
	cout<<"////////////////////////////////////////////////////////"<<endl;
	string test2 = "aaabcaa";
	map<string, int> m_map = FindAllSuccessSubString(test);

	string maxsub;
	int max = 0;

	for(map<string, int>::iterator iter = m_map.begin(); iter != m_map.end(); iter++)
	{
		if(iter->second > max)
		{
			max = iter->second;
			maxsub = iter->first;
		}
	}

	cout<<maxsub<<":"<<max<<endl;
	system("pause");
	return 0;
}

// 统计所有可能出现的子串
vector<string> FindAllSubString(string str)
{
	vector<string> result;
	int length = str.length();

	// 获得所有子串
	for(int i = 0; i <= length; i++)
	{
		// i 为子串的长度
		// 从原字符串的不同的位置获取子串
		for(int j = 0; j <= length - i; j++)
		{
			// 获取在偏移j 长度为 i的子串
			string sub = str.substr(j, i);
			bool flag = false;
			for(vector<string>::iterator iter = result.begin(); iter != result.end(); iter++)
			{
				if(*iter == sub)
				{
					flag = true;
					break;
				}
			}
			if(flag == false)
			{
				result.push_back(sub);
			}
		}
	}
	return result;
}


// 统计每个子串出现的次数
map<string, int> SubStrCount(string str)
{
	map<string, int> result;
	int length = str.length();
	for(int i = 1; i <= length; i++)
	{
		// i 为子串长度
		for(int j = 0; j<= length - i; j++)
		{
			// j 为计算子串的偏移量
			string sub = str.substr(j, i);
			result[sub]++;
		}
	}

	return result;
}

// 统计所有可能出现的子串
// 计算连续出现的子串的数量
map<string, int> FindAllSuccessSubString(string str)
{
	map<string, int> s_map;
	int length = str.length();

	// 获得所有子串
	for(int i = 1; i <= length; i++)
	{
		// i 为子串的长度
		// 从原字符串的不同的位置获取子串
		for(int j = 0; j <= length - i; j++)
		{
			// 获取在偏移j 长度为 i的子串
			string sub = str.substr(j, i);
			int subLength = sub.length();
			int count = 1; // 子串的连续出现的次数
			// 统计连续子串的出现次数
			int k;
			for(k = j + subLength; k <= length - subLength; k = k + subLength)
			{
				if(str.substr(k, subLength) == sub)
					count++;
				else
					break;
			}
			if(count > s_map[sub])
				s_map[sub] = count;
		}
	}
	return s_map;
}

<think> 我们正在讨论Stata中字符串操作。用户之前询问了统计字符串中特定字符或子字符串出现次数的方法。 根据引用[1],我们知道该文章介绍了字符串函数,包括长度获取、提取与查找等,但并没有直接给出统计出现次数的方法。 然而,我们可以利用Stata提供的字符串函数来构建这样的统计。常用的方法是结合`subinstr()`函数和`length()`函数: 1. 使用`subinstr()`函数将目标子字符串替换为空字符串(即删除),并记录替换的次数(通过调用该函数的第三个返回值)。 2. 或者,我们可以利用替换前后字符串长度的变化来计算出现次数:出现次数 = (原字符串长度 - 替换后字符串长度) / 目标子字符串的长度。 但是,需要注意的是,Stata的`subinstr()`函数本身可以返回替换的次数(通过第四个参数,该参数是一个宏,用于存储替换次数)。不过,这个功能在生成新变量时可能不太方便直接使用。 另一种方法是使用循环和`strpos()`函数来逐个查找子字符串的位置,每找到一次就计数,并更新查找的起始位置,直到找不到为止。 然而,更简洁的方法是使用`subinstr()`函数的一个特性:它有一个可选参数可以返回替换的次数。但请注意,在Stata中,`subinstr()`函数本身并不直接返回替换次数,而是通过宏来存储替换次数(在编程中使用循环时)?实际上,在Stata中,有一个函数`strrpos()`可以用于查找,但统计次数还是需要组合。 实际上,Stata提供了`subinstr()`函数,该函数有四个参数: subinstr(s, s1, s2, n) 其中,s是原字符串,s1是要被替换的子字符串,s2是替换成的字符串,n可以是替换的次数(比如1表示只替换第一个),也可以是.,表示替换所有。 这个函数返回替换后的字符串,并不返回替换的次数。 因此,我们通常采用长度差的方法: 公式: 出现次数 = (原字符串长度 - 替换后的字符串长度) / 目标子字符串的长度 具体步骤: 1. 计算原字符串的长度:`len_original = length(s)` 2. 将目标子字符串替换为空字符串:`s_new = subinstr(s, target, "", .)` // . 表示替换所有 3. 计算替换后字符串的长度:`len_new = length(s_new)` 4. 计算目标子字符串的长度:`len_target = length(target)` 5. 出现次数:`count = (len_original - len_new) / len_target` 注意:这个方法要求目标子字符串的长度至少为1,且当长度为0时(空字符串)没有意义。 另外,如果目标子字符串没有出现,则替换前后长度不变,计算结果为0,符合要求。 但是,这种方法有一个潜在的问题:如果目标子字符串出现重叠的情况,比如在字符串"aaa"中统计"aa"出现的次数,按照上述方法: 原字符串长度=3 替换后字符串:将"aa"替换为空,第一次替换前两个"a"得到"a",然后再替换?实际上,Stata的`subinstr()`函数在替换时是连续的,不会重叠。例如,在"aaa"中替换"aa",第一次替换后剩下"a",这时已经没有连续的"aa"了,所以只替换一次。因此,出现次数为1。 然而,如果用户希望统计重叠出现的次数,那么上述方法就不适用了。但通常我们统计的是不重叠的出现次数(即从左到右依次匹配,不重叠),所以上述方法符合常规。 因此,我们可以给出如下代码: 假设有一个变量名为`text`,我们要统计其中子字符串`"abc"`出现的次数(不重叠): gen len_original = length(text) gen text_new = subinstr(text, "abc", "", .) gen len_new = length(text_new) gen len_target = length("abc") // 或者直接使用3,因为"abc"长度为3 gen count = (len_original - len_new) / len_target 但是,为了避免除数为0,我们需要处理目标字符串为空的情况(空字符串在任意字符串中出现次数应该是无穷?但实际中我们不允许空字符串)。因此,可以加一个条件: gen count = . replace count = (len_original - len_new) / len_target if len_target>0 replace count = 0 if len_target==0 // 但实际上,目标字符串为空没有意义,通常我们不会统计字符串,所以我们这里可以视为0次。 另一种方法是使用循环(使用`strpos`函数): gen count = 0 gen start = 1 while (1) { gen pos = strpos(substr(text, start, .), "abc") if pos==0 break replace count = count + 1 replace start = start + pos + len_target - 1 // 注意:这里跳过已找到的位置,避免重叠;如果想重叠,则只需要跳过1个字符,但这里我们不重叠 } 但是,这个方法在Stata中实现循环变量更新比较麻烦,而且效率低,特别是当字符串很长时。 因此,我们推荐使用第一种方法(长度差法)。 另外,Stata 14及以后版本引入了`ustrlen`等Unicode字符串函数,如果字符串包含多字节字符(如中文),那么应该使用`ustr`系列函数。但是,对于英文字符串,`length`和`subinstr`就足够了。 如果字符串包含多字节字符,那么我们需要使用: len_original = ustrlen(text) text_new = usubinstr(text, target, "", .) // 注意:`usubinstr`是Unicode安全的替换函数 len_new = ustrlen(text_new) len_target = ustrlen(target) 然后计算count。 所以,我们根据字符串类型选择函数。 总结:在Stata中统计字符串中特定子字符串出现次数(不重叠)的方法主要是利用替换前后字符串长度的变化。 现在,我们给出一个示例代码(针对ASCII字符串,即不含多字节字符的情况): ``` * 假设数据集中有一个字符串变量text,目标子字符串为"abc" gen len_original = length(text) gen text_new = subinstr(text, "abc", "", .) gen len_new = length(text_new) gen count = (len_original - len_new) / length("abc") * 如果目标子字符串是变量,比如变量target,那么: * gen len_target = length(target) * 然后:gen count = (len_original - len_new) / len_target * 注意:如果target在某些观测中为空,那么len_target为0,会导致除以0错误。我们可以这样: gen count = cond(length(target)>0, (len_original - len_new)/length(target), 0) * 但是,通常target是给定的字符串,不是变量。如果是变量,并且有可能为空,那么需要处理。 * 另外,我们也可以在一行中完成: gen count = (length(text) - length(subinstr(text, "abc", "", .))) / length("abc") ``` 如果字符串包含中文等多字节字符,则使用: ``` gen count = (ustrlen(text) - ustrlen(usubinstr(text, "abc", "", .))) / ustrlen("abc") ``` 但是请注意:`usubinstr`函数是Stata14以后才引入的,所以如果使用旧版本,对于Unicode字符串可能有问题。 另外,如果目标子字符串中包含特殊字符(比如正则表达式元字符),`subinstr`函数不会将其视为正则表达式,而是字面字符串,所以安全。 最后,我们给出一个完整的例子: ``` * 创建示例数据 clear input str20 text "abc123abc" "hello world" "abcabcabc" "" end * 统计"abc"出现的次数 gen count = (length(text) - length(subinstr(text, "abc", "", .))) / length("abc") list ``` 输出结果应该是: 对于第一个观测:3个字符被替换掉两次(因为有两个"abc"),所以长度减少6,除以3得到2。 第二个观测:0 第三个观测:3 第四个观测:空字符串,长度为0,所以0。 因此,结果是正确的。 注意:如果目标子字符串的长度为0,那么上述计算会出现除以0的错误,所以我们需要确保目标子字符串非空。如果用户可能输入空字符串,那么需要先判断。 因此,我们可以这样写一个更健壮的版本: ``` capture drop count gen count = . * 假设目标子字符串是"abc",我们将其赋给一个局部宏 local target "abc" if length("`target'")>0 { replace count = (length(text) - length(subinstr(text, "`target'", "", .))) / length("`target'") } else { replace count = 0 // 或者缺失值,取决于你想如何处理空目标 } ``` 如果目标子字符串存储在变量`target`中,那么我们需要逐观测处理,因为每个观测的目标可能不同。此时,我们可以使用循环遍历每个观测: ``` gen count = . forvalues i=1/`c(N)' { local target = target[`i'] // 取出第i个观测的目标字符串 local len_target = length("`target'") if `len_target'>0 { replace count = (length(text) - length(subinstr(text, "`target'", "", .))) / `len_target' in `i' } else { replace count = 0 in `i' } } ``` 但是,这种方法在数据量大时可能较慢。我们可以尝试向量化操作,但Stata处理字符串时循环是常见的。 因此,我们推荐使用第一种方法(长度差法)来统计字符串的出现次数。 现在,我们回答用户的问题。 </think> 在Stata中统计字符串中特定字符或子字符串出现次数的方法主要基于字符串函数组合实现,以下是两种实用方法: ### 📌 方法1:使用`subinstr()`和字符串长度差计算 **原理**:通过替换目标子字符串为空值,计算替换前后的字符串长度差,再除以目标子字符串长度 **公式**: $$ \text{出现次数} = \frac{\text{原字符串长度} - \text{替换后字符串长度}}{\text{目标子字符串长度}} $$ **代码示例**: ```stata * 假设变量text存储原始字符串,查找子字符串"abc" gen orig_len = length(text) // 原始长度 gen new_text = subinstr(text, "abc", "", .) // 替换所有目标子串为空 gen new_len = length(new_text) // 替换后长度 gen target_len = length("abc") // 目标子串长度 gen count = (orig_len - new_len) / target_len // 出现次数 ``` ### ⚙️ 方法2:使用循环和`strpos()`函数(适用于复杂场景) **原理**:通过循环定位子字符串位置,逐次计数并更新搜索起点 ```stata gen count = 0 gen start = 1 // 起始搜索位置 gen pos = strpos(substr(text, start, .), "abc") // 查找子串位置 while pos != 0 { replace count = count + 1 replace start = start + pos + length("abc") - 1 // 更新搜索起点 replace pos = strpos(substr(text, start, .), "abc") // 继续查找 } ``` ### 💡 关键说明: 1. **长度差法适用场景**: - 推荐用于不重叠子串统计(默认行为) - 目标子串长度必须 ≥1,空子串无意义 - 使用`subinstr()`的`.`参数表示替换全部匹配项[^1] 2. **循环法适用场景**: - 需处理重叠匹配(如"aaa"中统计"aa"出现2次) - 支持动态调整搜索逻辑 - 但执行效率低于长度差法 3. **多字节字符处理**: ```stata gen orig_len = ustrlen(text) // Unicode字符长度 gen new_text = usubinstr(text, "目标", "", .) // Unicode安全替换 ``` ### 📊 示例演示 ```stata * 创建示例数据 clear input str20 text "apple_banana_apple" "stata_data_analysis" "重复重复重复" end * 统计"apple"出现次数 gen count = (length(text) - length(subinstr(text, "apple", "", .))) / length("apple") ``` 结果: | text | count | |---------------------|-------| | apple_banana_apple | 2 | | stata_data_analysis | 0 | | 重复重复重复 | 0 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值