1112. Stucked Keyboard (20)

修复键盘问题:找出坏键与原始字符串
解决计算机输入问题,分析重复字符确定键盘上可能卡住的按键,并重构原始输入字符串。针对给定的重复次数k,通过检查字符串中连续k倍数的字符来识别坏键,同时确保只输出一次每个坏键。实现过程中注意处理特殊情况,如已知好键和字符串操作的细节。简化代码方法是使用0和1表示坏键和好键。

1112. Stucked Keyboard (20)

时间限制
400 ms
内存限制
65536 kB
代码长度限制
16000 B
判题程序
Standard
作者
CHEN, Yue

On a broken keyboard, some of the keys are always stucked. So when you type some sentences, the characters corresponding to those keys will appear repeatedly on screen for k times.

Now given a resulting string on screen, you are supposed to list all the possible stucked keys, and the original string.

Notice that there might be some characters that are typed repeatedly. The stucked key will always repeat output for a fixed k times whenever it is pressed. For example, when k=3, from the string "thiiis iiisss a teeeeeest" we know that the keys "i" and "e" might be stucked, but "s" is not even though it appears repeatedly sometimes. The original string could be "this isss a teest".

Input Specification:

Each input file contains one test case. For each case, the 1st line gives a positive integer k ( 1<k<=100 ) which is the output repeating times of a stucked key. The 2nd line contains the resulting string on screen, which consists of no more than 1000 characters from {a-z}, {0-9} and "_". It is guaranteed that the string is non-empty.

Output Specification:

For each test case, print in one line the possible stucked keys, in the order of being detected. Make sure that each key is printed once only. Then in the next line print the original string. It is guaranteed that there is at least one stucked key.

Sample Input:
3
caseee1__thiiis_iiisss_a_teeeeeest
Sample Output:
ei
case1__this_isss_a_teest
题意:开头的keyboard很重要,一开始没认出是键盘看到样例的s就很疑惑,又读了一遍认出以后就秒懂题意。 有一把坏键盘,在输出时某几个特定的字符会连续打出k个,给定一条字符串,判断该字符串中有哪几个坏键(按出现顺序排列),并还原成正确的字符串

分析:如果某个字符是坏键,则当它出现时,一定有连续的n*k个,n>=1

           例如,k=2,aaaaabc,则abc均正常

           如果某个字符以好键的形式出现过,那么它一定是好键

           例如,k=3,aaabcda,则abcd均正常


           大概要注意的是这两个地方。做起来的感觉是,大致思路一下就有了,但是实现的时候会觉得很乱,各种要注意的地方很多,而且字符串操作太烦了,修修修修了半天,如果考试遇到这个肯定要跪。


#include<iostream>
#include<string>
using namespace std;
int map[500] = { 0 };//记录哪些字母or数字有问题 
int main()
{
	int k, i, j;
	cin >> k;
	string words, bad;
	cin >> words;
	for (i = 0; i<words.size(); i++)
	{
		j = 1;
		while (j < k && i + j < words.size()) {
			if (words[i] != words[i + j]) break;
			j++;//判断是否有k个连续,如果没有,break,导致j != k
		}
		if (j != k) {//说明words[i] 对应的 是好键
			map[(int)words[i]] = 1;// 1 代表好键
			if (bad.find(words[i]) != bad.npos)//假如输入aaabac,k=3,a开始会被判断成坏键,这里要把误判的修改回来 
				bad.erase(bad.find(words[i]));
		}
		else {
			if (map[(int)words[i]] != 1) {//如果已经确认是好键了,如abaaa,k=3,则不应该加入到坏键中
				map[(int)words[i]] = 2;// 2 代表坏键
				if (bad.find(words[i]) == bad.npos)//如果坏键中没有出现过该字符,则加入
					bad += words[i];
			}
			i += k - 1;//很重要,如aaabcd,k=3,假设这一轮i指向第一个a,下一轮i就应该指向b。
		}//如果i指向了第二个a,则a会被认为是好键
	}
	cout << bad << endl;
	for (i = 0; i<words.size(); i++)
		if (map[(int)words[i]] == 2) 
			words.erase(words.begin() + i + 1, words.begin() + i + k);
	cout << words << endl;
}


————————————————————————————————————————————————————

忽然发现,因为我的map只用了 1和2,所以实际上只要用 0和1,也能完美代替。

而且如果把0代表坏键,那么上面代码的第24行就不需要考虑有没有确认过是好键

以下是更改后的代码(其实就是删了两行然后改了一下最后的输出条件):

#include<iostream>
#include<string>
using namespace std;
int map[500] = { 0 };//记录哪些字母or数字有问题 ,0代表坏键,1代表好键
int main()
{
	int k, i, j;
	cin >> k;
	string words, bad;
	cin >> words;
	for (i = 0; i<words.size(); i++)
	{
		j = 1;
		while (j < k && i + j < words.size()) {
			if (words[i] != words[i + j]) break;
			j++;//判断是否有k个连续,如果没有,break,导致j != k
		}
		if (j != k) {//说明words[i] 对应的 是好键
			map[(int)words[i]] = 1;// 1 代表好键
			if (bad.find(words[i]) != bad.npos)//假如输入aaabac,k=3,a开始会被判断成坏键,这里要把误判的修改回来 
				bad.erase(bad.find(words[i]));
		}
		else {
			if (bad.find(words[i]) == bad.npos)//如果坏键中没有出现过该字符,则加入
				bad += words[i];
			i += k - 1;//很重要,如aaabcd,k=3,假设这一轮i指向第一个a,下一轮i就应该指向b。
		}//如果i指向了第二个a,则a会被认为是好键
	}
	cout << bad << endl;
	for (i = 0; i < words.size(); i++)
		if (!map[(int)words[i]])
			words.erase(words.begin() + i + 1, words.begin() + i + k);
	cout << words << endl;
}





<think>我们正在解决Go程序中由于goroutine卡住导致的panic问题,具体错误信息是"panic: stucked no response"。这通常意味着某个goroutine长时间没有响应,导致程序认为它已经卡死。以下是一些解决方法和预防措施: ### 1. **使用超时控制** 在可能导致阻塞的操作上设置超时是防止goroutine卡住的有效方法。可以使用`context`包来实现超时控制。 ```go ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // 将ctx传递给可能阻塞的函数 result, err := someBlockingFunction(ctx) if err != nil { // 处理错误,可能是超时错误 } ``` 如果函数在指定时间内没有返回,`ctx.Done()`通道会关闭,我们可以在函数中监听该通道以提前返回。 ### 2. **使用`select`和`time.After`** 对于没有使用`context`的简单场景,可以使用`select`和`time.After`实现超时: ```go ch := make(chan Result) go func() { // 执行可能阻塞的操作 ch <- someBlockingOperation() }() select { case res := <-ch: // 使用结果 case <-time.After(5 * time.Second): // 超时处理 } ``` ### 3. **检测和恢复** 我们可以启动一个监控goroutine来检测其他goroutine是否卡住。如果某个goroutine在预期时间内没有完成,我们可以选择重启它或者记录错误。 ```go go func() { for { select { case <-done: // 正常完成 return case <-time.After(10 * time.Second): // 超时,认为goroutine卡住,触发恢复操作 // 例如:记录日志、重启goroutine等 log.Println("goroutine stuck detected") // 这里可以采取进一步措施,比如重启这个goroutine } } }() ``` ### 4. **避免阻塞操作** 检查代码中可能导致长时间阻塞的操作,如无限制的通道操作、死锁、长时间运行的循环等。确保: - 通道操作有超时或取消机制。 - 避免在互斥锁中执行耗时操作。 - 使用带缓冲的通道或限制通道操作的数据量。 ### 5. **使用pprof和trace工具** Go提供了强大的工具来诊断goroutine卡住的问题: - **pprof**:可以查看当前所有goroutine的堆栈信息,帮助定位卡住的位置。 ```go import _ "net/http/pprof" ``` 启动HTTP服务器后,访问`/debug/pprof/goroutine?debug=1`可以查看goroutine堆栈。 - **trace**:可以捕获程序运行时的详细事件,用于分析goroutine的调度和阻塞情况。 ```go f, _ := os.Create("trace.out") trace.Start(f) defer trace.Stop() ``` ### 6. **使用`runtime.Goexit`** 如果某个goroutine需要被外部终止,可以考虑设计成可中断的。虽然Go没有直接终止goroutine的方法,但我们可以通过设置标志位来通知goroutine退出。 ```go type Worker struct { quit chan struct{} } func (w *Worker) Stop() { close(w.quit) } func (w *Worker) Run() { for { select { case <-w.quit: return default: // 执行任务 } } } ``` ### 7. **使用第三方库** 有一些第三方库可以帮助管理goroutine的生命周期和超时,例如: - [errgroup](https://pkg.go.dev/golang.org/x/sync/errgroup):可以控制一组goroutine,当其中一个出错时取消所有。 - [hystrix-go](https://github.com/afex/hystrix-go):实现超时和熔断。 ### 总结 解决goroutine卡住的问题主要从预防和诊断两方面入手。预防措施包括设置超时、避免阻塞操作、设计可退出的goroutine;诊断工具主要使用pprof和trace。通过以上方法,可以有效地减少"panic: stucked no response"错误的发生。 §§相关问题§§ 1. 如何在Go中实现goroutine的超时控制? 2. 如何使用pprof诊断goroutine泄漏? 3. Go中如何安全地终止一个正在运行的goroutine? 4. 什么是Go的context包,它有哪些用途? 5. 如何避免Go程序中的死锁?</think>### Go程序"panic: stucked no response"解决方法 当Go程序出现`panic: stucked no response`错误时,通常是由于某个goroutine长时间阻塞或未响应导致的。以下是系统性的解决方案: #### 1. **诊断卡住的goroutine** ```go import ( "runtime" "time" ) // 定期打印所有goroutine堆栈 go func() { for { time.Sleep(30 * time.Second) buf := make([]byte, 1<<20) // 1MB buffer stackSize := runtime.Stack(buf, true) fmt.Printf("=== Goroutine dump ===\n%s\n", buf[:stackSize]) } }() ``` 此代码会每30秒输出所有goroutine的堆栈跟踪,帮助定位卡住的位置[^1]。 #### 2. **关键修复方法** **(1) 为阻塞操作添加超时** ```go // 使用context设置超时 ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() select { case result := <-ch: // 正常处理 case <-ctx.Done(): return errors.New("operation timed out") } ``` **(2) 通道操作防护** ```go // 带超时的通道发送 func safeSend(ch chan<- int, value int, timeout time.Duration) bool { select { case ch <- value: return true case <-time.After(timeout): return false // 超时处理 } } // 带超时的通道接收 func safeRecv(ch <-chan int, timeout time.Duration) (int, bool) { select { case v := <-ch: return v, true case <-time.After(timeout): return 0, false // 超时处理 } } ``` **(3) 资源锁超时机制** ```go // 尝试获取锁(带超时) if !mutex.TryLock(2 * time.Second) { return errors.New("lock acquisition timeout") } defer mutex.Unlock() ``` **(4) 第三方库调用防护** ```go done := make(chan struct{}) go func() { thirdPartyBlockingCall() close(done) }() select { case <-done: // 正常完成 case <-time.After(5 * time.Second): // 强制终止或回滚 } ``` #### 3. **预防措施** - **启用pprof监控**: ```go import _ "net/http/pprof" go func() { log.Fatal(http.ListenAndServe(":6060", nil)) }() ``` 访问`http://localhost:6060/debug/pprof/goroutine?debug=1`查看goroutine状态[^2]。 - **使用goleak检测泄漏**: ```go import "go.uber.org/goleak" func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } ``` - **设置GODEBUG**: ```shell GODEBUG=gctrace=1,gcpacertrace=1 ./your_program ``` #### 4. **高级调试技巧** ```go // 在程序中添加诊断端点 http.HandleFunc("/debug/goroutines", func(w http.ResponseWriter, r *http.Request) { buf := make([]byte, 1<<20) stackSize := runtime.Stack(buf, true) w.Write(buf[:stackSize]) }) ``` **典型修复流程**: 1. 通过堆栈跟踪定位阻塞点 2. 添加超时控制逻辑 3. 使用`select`改造阻塞调用 4. 增加资源操作超时检查 5. 添加监控和诊断端点
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值