以前在OJ上做的题目(10)

    结束集训好多天了,做的题目也少了很多,这不这么多天才第一次写做题总结,上午看完每个星期必看的电视《篮球火》之后,又开始把做了好几天的那个题目拿出来看,终于让我把它给过了,呵呵,哈哈,好高兴。

TOJ 2808 String's Puzzle

    题目的意思是给出一个字母序列,求从字典序列最小开始,到达这个序列的个数差。如给出ABD,那么根据要求可以知道结果应为1,因为初始序列为ABC,只要一次就可以到达ABD。第一次做的时候是把所有经过的排列情况全部计算出来,虽然得到的结果是正确的,但这样会超时,因为花了大量的时间用来计算下一个排列,但事实上,题目只要求计算个数,并不要求计算出每一种排列。下面是这种计算所有排列的程序,虽然是超时,但我觉得还是很有价值,是求字典序排列的一种方法,跟以前自己用的数学方法有些差别,这个是用的递归。

#include <iostream>
#include <string>
using namespace std;

char str[27], goal[27];
bool used[26];
int len;

void renew(int pos)
{	//将pos后的位按从小到大重新分配字符
	memset(used, 0, sizeof(used));
	for(int i = 0; i <= pos; i++)//pos之前的字符标记为已被使用
		used[str[i]-'A'] = true;
	char ch = 'A';
	for(int i = pos+1; i < len; i++, ch++) {
		while(used[ch-'A']) 
			ch++;
		str[i] = ch;	
		used[ch-'A'] = true;
	}		
}

void next(int pos)
{	
	if(str[pos] == 'Z') {
		next(pos-1);
		return ;
	}
	//寻找一个在pos之前位置未出现且比str[pos]大的最小的字符
	bool find;	//标记是否找到
	char ch;
	for(ch = str[pos]+1; ch <= 'Z'; ch++) {
		find = true;
		for(int i = 0; i < pos; i++) {
			if(str[i] == ch) {
				find = false;
				break;	
			}	
		}
		if(find)
			break;
	}	
	if(find) {	//找到上面说的字符
		str[pos] = ch;
		renew(pos);
	} else { //对pos-1位进行操作
		next(pos-1);
	}
}

int main()
{
	while(cin>>len) {
		if(len == 0)
			break;
		getchar();
		gets(goal);
		
		//将数组进行初始化成最小的字典序排列
		for(int i = 0; i < len; i++) {
			str[i] = i+'A';
			used[str[i]-'A'] = true;
		}
		str[len] = '\0';
		
		//不断生成str字典排序的下一个字符串
		int ans = 0;
		while(strcmp(str, goal) != 0) {
			next(len-1);
			ans++;
			//test
			/*for(int i = 0; i < len; i++)
				cout<<str[i];
			cout<<endl;*/
		}
		
		cout<<ans<<endl;
		memset(used, 0, sizeof(used));
	}
	
	//system("pause");
	return 0;
}
 
<!---->#include  < iostream >
#include 
< string >
using   namespace  std;

char  str[ 27 ], goal[ 27 ];
bool  used[ 26 ];
int  len;

void  renew( int  pos)
{    
// 将pos后的位按从小到大重新分配字符
    memset(used,  0 sizeof (used));
    
for ( int  i  =   0 ; i  <=  pos; i ++ ) // pos之前的字符标记为已被使用
        used[str[i] - ' A ' =   true ;
    
char  ch  =   ' A ' ;
    
for ( int  i  =  pos + 1 ; i  <  len; i ++ , ch ++ ) {
        
while (used[ch - ' A ' ]) 
            ch
++ ;
        str[i] 
=  ch;    
        used[ch
- ' A ' =   true ;
    }        
}

void  next( int  pos)
{    
    
if (str[pos]  ==   ' Z ' ) {
        next(pos
- 1 );
        
return  ;
    }
    
// 寻找一个在pos之前位置未出现且比str[pos]大的最小的字符
     bool  find;     // 标记是否找到
     char  ch;
    
for (ch  =  str[pos] + 1 ; ch  <=   ' Z ' ; ch ++ ) {
        find 
=   true ;
        
for ( int  i  =   0 ; i  <  pos; i ++ ) {
            
if (str[i]  ==  ch) {
                find 
=   false ;
                
break ;    
            }    
        }
        
if (find)
            
break ;
    }    
    
if (find) {     // 找到上面说的字符
        str[pos]  =  ch;
        renew(pos);
    } 
else  {  // 对pos-1位进行操作
        next(pos - 1 );
    }
}

int  main()
{
    
while (cin >> len) {
        
if (len  ==   0 )
            
break ;
        getchar();
        gets(goal);
        
        
// 将数组进行初始化成最小的字典序排列
         for ( int  i  =   0 ; i  <  len; i ++ ) {
            str[i] 
=  i + ' A ' ;
            used[str[i]
- ' A ' =   true ;
        }
        str[len] 
=   ' \0 ' ;
        
        
// 不断生成str字典排序的下一个字符串
         int  ans  =   0 ;
        
while (strcmp(str, goal)  !=   0 ) {
            next(len
- 1 );
            ans
++ ;
            
// test
             /* for(int i = 0; i < len; i++)
                cout<<str[i];
            cout<<endl;
*/
        }
        
        cout
<< ans << endl;
        memset(used, 
0 sizeof (used));
    }
    
    
// system("pause");
     return   0 ;
}

 

    在这种思路没有成功且只要求计算个数的前提下,开始想数学的方法,也就是说,直接根据输入然后用排列组合的知道计算个数。如给出字符串ADB,那么可以这样来考虑,第一个字符为A,那么不可能有其他的情况,不计,第二位为D,前面没有使用的有B和C,那么就有两个,然后其后还有一位,就是从另外24个中取一个,这样的话,就是2*24,同样,其他的也可以这样来算。但是26的全排列会很大,超出了__int64所能表示的范围,故而要么用高精度,要么用java中BigInteger 。Java版本的时候出了点问题,希望自己尽快整好,下面附上C++版本。

#include <iostream>
using namespace std;

char instr[26];
int	tmparr[50], tmp[50], len,
 	ans[50]; 	//保存最终计算的结果
bool used[26];	//标记字母是否被使用

void mutiply(int num)
{	//将数值num与数组tmparr相乘,结果保存在tmparr中
	if(num == 0) {
		memset(tmparr, 0, sizeof(tmparr));
		return ;	
	}
	int num1 = num%10, highest = 0;
	//找到tmparr中的最高位
	while(tmparr[highest] == 0)
		highest++;
	memset(tmp, 0, sizeof(tmp));
	int tag = 0;	//标记进位
	for(int i = 49; i >= highest; i--) {
		int tmpint = num1*tmparr[i]+tag;
		tmp[i] = tmpint%10;
		tag = tmpint/10;
	}
	if(tag > 0) 
		tmp[highest-1] = tag;
	if(num >= 10) {	//num为两位数,则高位也要分别相乘
		num1 = num/10;
		tag = 0;
		for(int i = 49; i >= highest; i--) {
			int tmpint = (num1*tmparr[i]+tag);
			tmp[i-1] += tmpint%10;
			tag = tmpint/10;	
		}
		if(tag > 0)
			tmp[highest-2] = tag;
	}
	for(int i = 0; i < 50; i++)
		tmparr[i] = tmp[i];	
}

void calPer(int low, int high)
{	//计算以low为下标,high为上标的排列的值,最终保存在tmparr中
	memset(tmparr, 0, sizeof(tmparr));
	if((0 == low) || (0 == high))
		return ;
	tmparr[49] = low%10;	//low最多为25
	tmparr[48] = low/10;
	for(int i = 1; i < high; i++)
		mutiply(--low);	
}

void add()
{	//将上一步计算出来保存在tmparr中结果加到ans中
	int tag = 0;
	for(int i = 49; i >= 0; i--) {
		int tmpint = ans[i]+tmparr[i]+tag;
		ans[i] = tmpint%10;
		tag = tmpint/10;
	}
}

int main()
{
	while(cin>>len) {
		if(len == 0) 
			break;
		getchar();
		gets(instr);
		for(int i = 0; i < len; i++) {
			int times = 0;
			//计算在第i位上可能的情况数
			for(char ch = 'A'; ch < instr[i]; ch++) 
				if(!used[ch-'A']) 
					times++;
			//cout<<"times---"<<times<<endl;
			used[instr[i]-'A'] = true;
			if(0 != times) {//如果times为0,则最后还是不会改变,可直接不计算
				if(len == i+1) {//当为最后一位时,求排列得到结果为0,故需另外处理
					memset(tmparr, 0, sizeof(tmparr));
					tmparr[49] = times%10;
					tmparr[48] = times/10;
					add();
					continue;	
				}
				calPer(25-i, len-i-1);  
				//乘上可能的情况数times
				mutiply(times);			
				add();
			}
		}
		//output
		int tag = 0;
		while((ans[tag] == 0) && (tag < 50))
			tag++;
		if(tag == 50) 
			printf("0");
		while(tag < 50)
			printf("%d", ans[tag++]);
		printf("\n");
		memset(used, 0, sizeof(used));
		memset(ans, 0, sizeof(ans));
	}
	
	//system("pause");
	return 0;
}
 
<!---->#include  < iostream >
using   namespace  std;

char  instr[ 26 ];
int     tmparr[ 50 ], tmp[ 50 ], len,
     ans[
50 ];      // 保存最终计算的结果
bool  used[ 26 ];     // 标记字母是否被使用

void  mutiply( int  num)
{    
// 将数值num与数组tmparr相乘,结果保存在tmparr中
     if (num  ==   0 ) {
        memset(tmparr, 
0 sizeof (tmparr));
        
return  ;    
    }
    
int  num1  =  num % 10 , highest  =   0 ;
    
// 找到tmparr中的最高位
     while (tmparr[highest]  ==   0 )
        highest
++ ;
    memset(tmp, 
0 sizeof (tmp));
    
int  tag  =   0 ;     // 标记进位
     for ( int  i  =   49 ; i  >=  highest; i -- ) {
        
int  tmpint  =  num1 * tmparr[i] + tag;
        tmp[i] 
=  tmpint % 10 ;
        tag 
=  tmpint / 10 ;
    }
    
if (tag  >   0
        tmp[highest
- 1 =  tag;
    
if (num  >=   10 ) {     // num为两位数,则高位也要分别相乘
        num1  =  num / 10 ;
        tag 
=   0 ;
        
for ( int  i  =   49 ; i  >=  highest; i -- ) {
            
int  tmpint  =  (num1 * tmparr[i] + tag);
            tmp[i
- 1 +=  tmpint % 10 ;
            tag 
=  tmpint / 10 ;    
        }
        
if (tag  >   0 )
            tmp[highest
- 2 =  tag;
    }
    
for ( int  i  =   0 ; i  <   50 ; i ++ )
        tmparr[i] 
=  tmp[i];    
}

void  calPer( int  low,  int  high)
{    
// 计算以low为下标,high为上标的排列的值,最终保存在tmparr中
    memset(tmparr,  0 sizeof (tmparr));
    
if (( 0   ==  low)  ||  ( 0   ==  high))
        
return  ;
    tmparr[
49 =  low % 10 ;     // low最多为25
    tmparr[ 48 =  low / 10 ;
    
for ( int  i  =   1 ; i  <  high; i ++ )
        mutiply(
-- low);    
}

void  add()
{    
// 将上一步计算出来保存在tmparr中结果加到ans中
     int  tag  =   0 ;
    
for ( int  i  =   49 ; i  >=   0 ; i -- ) {
        
int  tmpint  =  ans[i] + tmparr[i] + tag;
        ans[i] 
=  tmpint % 10 ;
        tag 
=  tmpint / 10 ;
    }
}

int  main()
{
    
while (cin >> len) {
        
if (len  ==   0
            
break ;
        getchar();
        gets(instr);
        
for ( int  i  =   0 ; i  <  len; i ++ ) {
            
int  times  =   0 ;
            
// 计算在第i位上可能的情况数
             for ( char  ch  =   ' A ' ; ch  <  instr[i]; ch ++
                
if ( ! used[ch - ' A ' ]) 
                    times
++ ;
            
// cout<<"times---"<<times<<endl;
            used[instr[i] - ' A ' =   true ;
            
if ( 0   !=  times) { // 如果times为0,则最后还是不会改变,可直接不计算
                 if (len  ==  i + 1 ) { // 当为最后一位时,求排列得到结果为0,故需另外处理
                    memset(tmparr,  0 sizeof (tmparr));
                    tmparr[
49 =  times % 10 ;
                    tmparr[
48 =  times / 10 ;
                    add();
                    
continue ;    
                }
                calPer(
25 - i, len - i - 1 );  
                
// 乘上可能的情况数times
                mutiply(times);            
                add();
            }
        }
        
// output
         int  tag  =   0 ;
        
while ((ans[tag]  ==   0 &&  (tag  <   50 ))
            tag
++ ;
        
if (tag  ==   50
            printf(
" 0 " );
        
while (tag  <   50 )
            printf(
" %d " , ans[tag ++ ]);
        printf(
" \n " );
        memset(used, 
0 sizeof (used));
        memset(ans, 
0 sizeof (ans));
    }
    
    
// system("pause");
     return   0 ;
}


TOJ 1630 String Matching

    记得POJ上也有个一样的题目,我是用得最基础也是最容易理解的方法,就是把一个固定,然后让另一个从一边逐个移到另一边,在这个移动的过程中计算,程序中花了很多代码在预处理跟结果格式的处理上。

#include <iostream>
#include <string>
using namespace std;

int main()
{
	int sameletter;
	char shortone[100], longone[100];
	while(cin>>shortone) {
		if(!strcmp(shortone, "-1"))
			break;
		cin>>longone;
		if(!strcmp(shortone, longone)) {
			printf("appx(%s,%s) = 1\n", shortone, shortone);
			continue;
		}
		int longlen = strlen(longone), shortlen = strlen(shortone);
		bool changtag = false;	//标记两者的长度是否被改变过
		if(shortlen > longlen) {//使两个字符串的长度与名字相同
			char temp[100];
			strcpy(temp, longone);
			temp[longlen] = '\0';
			strcpy(longone, shortone);
			longone[shortlen] = '\0';
			strcpy(shortone, temp);
			shortone[longlen] = '\0';
			int inttemp = shortlen ;
			shortlen = longlen;
			longlen = inttemp;
			changtag = true;
		}

		sameletter = 0;
		int currentsame = 0;
		for(int i = 0; i < shortlen; i++) {
			for(int j = 0; j < longlen; j++) {//设定一个初始匹配位置
				if(shortone[i] == longone[j]) {
					currentsame = 1;
					int ks = i+1, kl = j+1;
					while((ks < shortlen) && (kl < longlen)) {//计算相同的字符的个数 
						if(shortone[ks] == longone[kl])
							currentsame++;
						ks++, kl++;
					}
					if(currentsame > sameletter)//相同字符个数的修改
						sameletter = currentsame;
				}
			}
		}
		
		if(sameletter == 0) {
			if(changtag)
				cout<<"appx("<<longone<<','<<shortone<<") = 0"<<endl;
			else
				cout<<"appx("<<shortone<<','<<longone<<") = 0"<<endl;
		} else {//分子与分母可约分的情况
			int fenzi = sameletter*2, fenmu = shortlen+longlen;
			int i = 2;
			while(i <= fenzi) {
				while((fenzi%i == 0) && (fenmu%i == 0)) {
					fenzi = fenzi/i;
					fenmu = fenmu/i;
				}
					i++;
			}
			if(changtag)
				cout<<"appx("<<longone<<','<<shortone<<") = "<<fenzi<<'/'<<fenmu<<endl;
			else
				cout<<"appx("<<shortone<<','<<longone<<") = "<<fenzi<<'/'<<fenmu<<endl;
		}
	}
	
	//system("pause");
	return 0;
}
 
<!---->#include  < iostream >
#include 
< string >
using   namespace  std;

int  main()
{
    
int  sameletter;
    
char  shortone[ 100 ], longone[ 100 ];
    
while (cin >> shortone) {
        
if ( ! strcmp(shortone,  " -1 " ))
            
break ;
        cin
>> longone;
        
if ( ! strcmp(shortone, longone)) {
            printf(
" appx(%s,%s) = 1\n " , shortone, shortone);
            
continue ;
        }
        
int  longlen  =  strlen(longone), shortlen  =  strlen(shortone);
        
bool  changtag  =   false ;     // 标记两者的长度是否被改变过
         if (shortlen  >  longlen) { // 使两个字符串的长度与名字相同
             char  temp[ 100 ];
            strcpy(temp, longone);
            temp[longlen] 
=   ' \0 ' ;
            strcpy(longone, shortone);
            longone[shortlen] 
=   ' \0 ' ;
            strcpy(shortone, temp);
            shortone[longlen] 
=   ' \0 ' ;
            
int  inttemp  =  shortlen ;
            shortlen 
=  longlen;
            longlen 
=  inttemp;
            changtag 
=   true ;
        }

        sameletter 
=   0 ;
        
int  currentsame  =   0 ;
        
for ( int  i  =   0 ; i  <  shortlen; i ++ ) {
            
for ( int  j  =   0 ; j  <  longlen; j ++ ) { // 设定一个初始匹配位置
                 if (shortone[i]  ==  longone[j]) {
                    currentsame 
=   1 ;
                    
int  ks  =  i + 1 , kl  =  j + 1 ;
                    
while ((ks  <  shortlen)  &&  (kl  <  longlen)) { // 计算相同的字符的个数 
                         if (shortone[ks]  ==  longone[kl])
                            currentsame
++ ;
                        ks
++ , kl ++ ;
                    }
                    
if (currentsame  >  sameletter) // 相同字符个数的修改
                        sameletter  =  currentsame;
                }
            }
        }
        
        
if (sameletter  ==   0 ) {
            
if (changtag)
                cout
<< " appx( " << longone << ' , ' << shortone << " ) = 0 " << endl;
            
else
                cout
<< " appx( " << shortone << ' , ' << longone << " ) = 0 " << endl;
        } 
else  { // 分子与分母可约分的情况
             int  fenzi  =  sameletter * 2 , fenmu  =  shortlen + longlen;
            
int  i  =   2 ;
            
while (i  <=  fenzi) {
                
while ((fenzi % ==   0 &&  (fenmu % ==   0 )) {
                    fenzi 
=  fenzi / i;
                    fenmu 
=  fenmu / i;
                }
                    i
++ ;
            }
            
if (changtag)
                cout
<< " appx( " << longone << ' , ' << shortone << " ) =  " << fenzi << ' / ' << fenmu << endl;
            
else
                cout
<< " appx( " << shortone << ' , ' << longone << " ) =  " << fenzi &
0
0
分享到:
评论

你可能感兴趣的:(J#)