题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=1106
其实挺简单的,开始的时候使用了string,结果超时,然后查了一下,看有使用函数strtok和sscanf的,百度了一下,以前都不知道,学习了。
char *strtok(char s[], const char *delim);
功能是分解字符串为一组字符串。s为要分解的字符串,delim为分隔符字符串。
在这篇帖子:http://www.cnblogs.com/hoys/archive/2011/09/19/2180999.html有很详细的解释。
PS:总是感觉linux的string.h文件中的实现比较容易看,VS中是在strtok_s.inl文件中实现的,可以跟踪进去看看源码。
不过其中有些语句看不明白,拿出来大家讨论讨论:
下面这个是VS中的实现:
_FUNC_PROLOGUE char * __cdecl _FUNC_NAME(char *_String, const char *_Control, char **_Context) { unsigned char *str; const unsigned char *ctl = _Control; unsigned char map[32]; int count; /* validation section */ _VALIDATE_POINTER_ERROR_RETURN(_Context, EINVAL, NULL); _VALIDATE_POINTER_ERROR_RETURN(_Control, EINVAL, NULL); _VALIDATE_CONDITION_ERROR_RETURN(_String != NULL || *_Context != NULL, EINVAL, NULL); /* Clear control map */ for (count = 0; count < 32; count++) { map[count] = 0; } /* Set bits in delimiter table */ do { map[*ctl >> 3] |= (1 << (*ctl & 7)); } while (*ctl++); /* If string is NULL, set str to the saved * pointer (i.e., continue breaking tokens out of the string * from the last strtok call) */ if (_String != NULL) { str = _String; } else { str = *_Context; } /* Find beginning of token (skip over leading delimiters). Note that * there is no token iff this loop sets str to point to the terminal * null (*str == 0) */ while ((map[*str >> 3] & (1 << (*str & 7))) && *str != 0) { str++; } _String = str; /* Find the end of the token. If it is not the end of the string, * put a null there. */ for ( ; *str != 0 ; str++ ) { if (map[*str >> 3] & (1 << (*str & 7))) { *str++ = 0; break; } } /* Update context */ *_Context = str; /* Determine if a token has been found. */ if (_String == str) { return NULL; } else { return _String; } }
do { map[*ctl >> 3] |= (1 << (*ctl & 7)); } while (*ctl++);
这种实现的原理是什么,不理解?
刚发帖提问,有大神解释了,讨论帖:http://bbs.csdn.net/topics/390502089
我摘抄过来,果然实现的好巧妙哦:
map是个位集,每个字节存着8个标志位,一共32*8=256个标志位
map[*ctl >> 3] |= (1 << (*ctl & 7));
就是把map中第*ctl个标志位设为1
索引号除以8(>>3),算出是第几个字节
索引号模8(&0x07),算出是第几位
1 << 位数,得到一个相应位为1其它位为0的数值。
回到原题目,strtok函数会根据提供的分割字符修改原始字符串,其中使用了一个静态变量指针来记录下一个字符串的起始位置,查看strtok函数会看到:贴出来微软的实现,这里边涉及到了多线程的问题:
#ifdef _SECURE_VERSION char * __cdecl strtok_s ( char * string, const char * control, char ** context ) #else /* _SECURE_VERSION */ char * __cdecl strtok ( char * string, const char * control ) #endif /* _SECURE_VERSION */ { unsigned char *str; const unsigned char *ctrl = control; unsigned char map[32]; int count; #ifdef _SECURE_VERSION /* validation section */ _VALIDATE_RETURN(context != NULL, EINVAL, NULL); _VALIDATE_RETURN(string != NULL || *context != NULL, EINVAL, NULL); _VALIDATE_RETURN(control != NULL, EINVAL, NULL); /* no static storage is needed for the secure version */ #else /* _SECURE_VERSION */ _ptiddata ptd = _getptd(); #endif /* _SECURE_VERSION */ /* Clear control map */ for (count = 0; count < 32; count++) map[count] = 0; /* Set bits in delimiter table */ do { map[*ctrl >> 3] |= (1 << (*ctrl & 7)); } while (*ctrl++); /* Initialize str */ /* If string is NULL, set str to the saved * pointer (i.e., continue breaking tokens out of the string * from the last strtok call) */ if (string) str = string; else str = _TOKEN;//这个是个宏定义,在前边有,是从当前线程的数据结构中取出来。 /* Find beginning of token (skip over leading delimiters). Note that * there is no token iff this loop sets str to point to the terminal * null (*str == '\0') */ while ( (map[*str >> 3] & (1 << (*str & 7))) && *str ) str++; string = str; /* Find the end of the token. If it is not the end of the string, * put a null there. */ for ( ; *str ; str++ ) if ( map[*str >> 3] & (1 << (*str & 7)) ) { *str++ = '\0'; break; } /* Update nextoken (or the corresponding field in the per-thread data * structure */ _TOKEN = str;//存储 /* Determine if a token has been found. */ if ( string == str ) return NULL; else return string; }
if (string) str = string; else str = _TOKEN;
仔细看代码,(其他的跟strtok_s差不多)我只是分割字符串,整个_ptiddata ptd = _getptd()干什么,人家linux中是用静态指针来实现的,但是微软的实现中没有找到这个变量。其实不然,虽然没有明显的static,但是看他的注释发现是存储在线程的数据结构中的。
/* Update nextoken (or the corresponding field in the per-thread data * structure */ _TOKEN = str;//存储
#ifdef _SECURE_VERSION #define _TOKEN *context #else /* _SECURE_VERSION */ #define _TOKEN ptd->_token
_ptiddata ptd = _getptd_noexit(); if (!ptd) { _amsg_exit(_RT_THREAD); /* write message and die */ } return ptd;
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn=1009; char str[maxn]; int a[maxn]; char *p; int n; int i; int main() { while(cin >> str) { p=strtok(str,"5"); n=0; while(p) { sscanf(p,"%d",&a[++n]); p=strtok(NULL,"5"); } sort(a+1,a+1+n); for(i=1;i<=n;i++) { if(i!=1) cout << ' '; cout << a[i]; } cout << endl; } return 0; }